v1.2.0
Adds production-grade engineering features and tools: 2D GD&T (feature control frames + datum features) via engineering/gdt.py, ISO‑129 dimension tolerance support (engineering/tolerances.py) and an ezdxf/COM plumbing for toleranced dims, a scalar drawing score + invalidity ratio (engineering/scoring.py) wired into drawing_finalize, in-place entity editors (entity_edit_text/entity_edit_geometry), and a friendly drawing_settings facade. Includes server tool wiring, COM/ezdxf backend implementations, datum-consistency critique, many unit tests, README/CLAUDE/CHANGELOG updates, and a version bump to 1.2.0. Also corrects COM backend capability label (entities_2d).
This commit is contained in:
@@ -0,0 +1,85 @@
|
||||
"""Scalar drawing-quality score + invalidity ratio.
|
||||
|
||||
Every 2025-26 CAD-generation benchmark (MUSE, CadBench) grades an *Invalidity
|
||||
Ratio*, not just shape similarity. This module turns the validator's and
|
||||
critique's severity counts into a single regression-trackable scalar so the
|
||||
premium workflow has an objective function (the prerequisite for an automated
|
||||
refiner loop and for smoke-test regression tracking).
|
||||
|
||||
The weights are a documented convention, not ground truth — they exist so the
|
||||
number moves in the right direction (errors hurt most, info barely registers)
|
||||
and can be asserted in tests.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
# Penalty weights per severity. Errors dominate; info is nearly free.
|
||||
_W_ERROR = 25.0
|
||||
_W_WARNING = 8.0
|
||||
_W_INFO = 1.0
|
||||
|
||||
|
||||
def score_findings(
|
||||
errors: int,
|
||||
warnings: int,
|
||||
info: int = 0,
|
||||
*,
|
||||
w_error: float = _W_ERROR,
|
||||
w_warning: float = _W_WARNING,
|
||||
w_info: float = _W_INFO,
|
||||
) -> dict:
|
||||
"""Collapse severity counts into a 0-100 score + invalidity ratio.
|
||||
|
||||
- ``score``: ``100 - (errors·Wₑ + warnings·Wᵥᵥ + info·Wᵢ)`` clamped to [0, 100].
|
||||
A clean drawing scores 100.
|
||||
- ``invalidity_ratio``: ``errors / max(1, total_findings)`` — the fraction of
|
||||
findings that are hard failures (0.0 when there are no findings).
|
||||
- ``grade``: coarse A-F bucket for at-a-glance reporting.
|
||||
"""
|
||||
errors = max(0, int(errors))
|
||||
warnings = max(0, int(warnings))
|
||||
info = max(0, int(info))
|
||||
|
||||
penalty = errors * w_error + warnings * w_warning + info * w_info
|
||||
score = max(0.0, min(100.0, 100.0 - penalty))
|
||||
|
||||
total = errors + warnings + info
|
||||
invalidity_ratio = (errors / total) if total else 0.0
|
||||
|
||||
return {
|
||||
"score": round(score, 1),
|
||||
"invalidity_ratio": round(invalidity_ratio, 3),
|
||||
"grade": _grade(score),
|
||||
"errors": errors,
|
||||
"warnings": warnings,
|
||||
"info": info,
|
||||
}
|
||||
|
||||
|
||||
def combine(*severity_dicts: dict) -> dict:
|
||||
"""Sum several ``{'error': n, 'warning': n, 'info': n}`` summaries then score.
|
||||
|
||||
Accepts the shapes emitted by both ``DrawingValidator`` (``summary``) and the
|
||||
finalize ``critique_summary`` so the finalize gate can score the union of the
|
||||
structural validator and the premium critique in one number.
|
||||
"""
|
||||
errors = warnings = info = 0
|
||||
for d in severity_dicts:
|
||||
if not d:
|
||||
continue
|
||||
errors += int(d.get("error", 0))
|
||||
warnings += int(d.get("warning", 0))
|
||||
info += int(d.get("info", 0))
|
||||
return score_findings(errors, warnings, info)
|
||||
|
||||
|
||||
def _grade(score: float) -> str:
|
||||
if score >= 95.0:
|
||||
return "A"
|
||||
if score >= 85.0:
|
||||
return "B"
|
||||
if score >= 70.0:
|
||||
return "C"
|
||||
if score >= 50.0:
|
||||
return "D"
|
||||
return "F"
|
||||
Reference in New Issue
Block a user