"""Item library for control_test: meshes are DISCOVERED in items/, classes are READ from items/labels.json. No geometric auto-measurement. Dimensions and k are taken from the labelling, which is the ground truth this cell is verified against - measuring them from the mesh was tried and the roundness estimate under-read handled/hollow bodies (bucket 0.737 vs 0.995, mug 0.731 vs 0.985), i.e. class D silently became B. Reading the label removes that whole class of error from the mechanics test. The folder is still the source of items: drop a .usd in, add one line to labels.json, and it joins the next run. Anything in the folder without a label is reported and skipped rather than guessed at. The documented rules are kept in `classify()` so a labelling can be checked for internal consistency (`verify_labels()`), not to derive it: D "не подходит без доупаковки" габариты как у B, но k > 0.8 хотя бы в одном сечении C "не подходит по габаритам" любой размер < 10 мм ИЛИ не влезает в 450x320x320 мм. Форма не важна. B "подходит для сортировки" всё от 10x10x10 до 450x320x320 мм и k <= 0.8 Fit is tested with the item's extents and the envelope both sorted descending - a parcel may be presented on any face. """ from __future__ import annotations import json import pathlib ENVELOPE_MM = sorted((450.0, 320.0, 320.0), reverse=True) MIN_DIM_MM = 10.0 K_THRESHOLD = 0.8 LABELS_FILE = "labels.json" def classify(dims_mm, k): """the documented decision, D checked first""" d = sorted(dims_mm, reverse=True) undersize = min(d) < MIN_DIM_MM fits = all(a <= b + 1e-9 for a, b in zip(d, ENVELOPE_MM)) if undersize or not fits: return "C" # shape irrelevant return "D" if k > K_THRESHOLD else "B" def load_library(items_dir): """every .usd in items_dir, sorted, paired with its label. Returns dicts with name/path/cls/dims_mm/k, or name/path/error for meshes that have no entry in labels.json - those are skipped by the runner, never guessed. """ items_dir = pathlib.Path(items_dir) labels_path = items_dir / LABELS_FILE if not labels_path.exists(): raise FileNotFoundError(f"{labels_path} missing - the item classes live there") labels = json.loads(labels_path.read_text()) out = [] for f in sorted(items_dir.glob("*.usd")): rec = labels.get(f.stem) if rec is None: out.append(dict(name=f.stem, path=str(f), error="no entry in labels.json")) continue out.append(dict(name=f.stem, path=str(f), cls=rec["zone"], dims_mm=rec.get("dims_mm"), k=rec.get("k"))) return out def verify_labels(items_dir): """check each label against the documented rules; returns the rows that disagree. A label whose own dims/k imply a different class is a labelling bug, and it would otherwise show up as a mysterious mechanical failure. """ bad = [] for r in load_library(items_dir): if "error" in r or r.get("dims_mm") is None or r.get("k") is None: continue implied = classify(r["dims_mm"], r["k"]) if implied != r["cls"]: bad.append(dict(name=r["name"], labelled=r["cls"], implied=implied, dims_mm=r["dims_mm"], k=r["k"])) return bad