rawwerks / model-compare

Compare 3D CAD models using boolean operations (IoU, Dice, precision/recall). Use when evaluating generated models against gold references, diffing CAD revisions, or computing similarity metrics for ML training. Triggers on: model diff, compare models, IoU, intersection over union, model similarity, CAD comparison, STEP diff, 3D evaluation, gold reference, generated model, precision recall 3D.

0 views
0 installs

Skill Content

---
name: model-compare
description: Compare 3D CAD models using boolean operations (IoU, Dice, precision/recall). Use when evaluating generated models against gold references, diffing CAD revisions, or computing similarity metrics for ML training. Triggers on: model diff, compare models, IoU, intersection over union, model similarity, CAD comparison, STEP diff, 3D evaluation, gold reference, generated model, precision recall 3D.
---

# 3D Model Comparison Tool

Compare CAD models using boolean operations to compute similarity metrics like IoU, Dice, precision, and recall. Useful for:

- Evaluating ML-generated models against gold references
- Comparing revisions of CAD designs
- Computing metrics for training 3D generative models
- Visualizing geometric differences

## Quick Start

```bash
# Compare two STEP files
uvx --from build123d python scripts/model_diff.py reference.step generated.step

# JSON output for training pipelines
uvx --from build123d python scripts/model_diff.py ref.step gen.step --json --no-export

# Demo mode (no files needed)
uvx --from build123d python scripts/model_diff.py --demo
```

## Supported Formats

| Format | Extension | Notes |
|--------|-----------|-------|
| STEP | `.step`, `.stp` | Recommended - full CAD fidelity |
| BREP | `.brep` | OpenCASCADE native format |
| STL | `.stl` | Mesh format - may have boolean issues |

## Output Metrics

### Primary Metrics (for ML training)

| Metric | Range | Description |
|--------|-------|-------------|
| **IoU** (Jaccard) | 0-1 | `|A∩B| / |A∪B|` - standard similarity |
| **Dice** (F1) | 0-1 | `2|A∩B| / (|A|+|B|)` - more sensitive to small overlaps |
| **Precision** | 0-1 | `|A∩B| / |B|` - how much of generated is correct |
| **Recall** | 0-1 | `|A∩B| / |A|` - how much of reference was captured |

### Diagnostic Metrics

| Metric | Description |
|--------|-------------|
| `volume_ratio` | B/A volume ratio (1.0 = same size) |
| `center_offset` | Distance between centers of mass |
| `bbox_iou` | Bounding box IoU (coarse alignment) |
| `size_ratio_x/y/z` | Per-axis scale comparison |
| `surface_ratio` | Surface area comparison |

### Interpretation

The tool provides automatic interpretation:
- **Over-generating**: Low precision, high extra geometry
- **Under-generating**: Low recall, missing geometry
- **Size issues**: Volume ratio far from 1.0
- **Position issues**: Large center offset

## CLI Options

```
usage: model_diff.py [-h] [-o OUTPUT_DIR] [--json] [--no-export] [--demo]
                     [reference] [generated]

positional arguments:
  reference          Reference/gold model file (STEP, BREP, or STL)
  generated          Generated/predicted model file to compare

options:
  -o, --output-dir   Output directory for GLB files (default: .)
  --json             Output only JSON metrics (for pipelines)
  --no-export        Skip exporting GLB visualization files
  --demo             Run with built-in demo models
```

## Output Files

When `--no-export` is not set, produces GLB files for visualization:

| File | Description |
|------|-------------|
| `diff_reference.glb` | The reference model (A) |
| `diff_generated.glb` | The generated model (B) |
| `diff_missing.glb` | Geometry in A but not B (under-generation) |
| `diff_extra.glb` | Geometry in B but not A (over-generation) |
| `diff_common.glb` | Geometry in both (correct match) |

## Example: Training Pipeline Integration

```bash
# Batch evaluation
for gen in outputs/*.step; do
    uvx --from build123d python model_diff.py gold.step "$gen" --json --no-export
done | jq -s '{
    avg_iou: (map(.iou) | add / length),
    avg_precision: (map(.precision) | add / length),
    avg_recall: (map(.recall) | add / length)
}'
```

## Example: Loss Function

```python
# In your training code, use metrics for loss:
loss = (
    (1 - metrics['iou']) * 1.0 +           # Primary shape match
    abs(1 - metrics['volume_ratio']) * 0.5 + # Scale accuracy
    metrics['center_offset'] * 0.1           # Position accuracy
)
```

## How It Works

The tool uses boolean operations from OpenCASCADE (via build123d):

```
Missing  = Reference - Generated  (A - B)
Extra    = Generated - Reference  (B - A)
Common   = Reference & Generated  (A ∩ B)
Union    = Reference + Generated  (A ∪ B)

IoU      = volume(Common) / volume(Union)
Dice     = 2 * volume(Common) / (volume(A) + volume(B))
Precision = volume(Common) / volume(B)
Recall    = volume(Common) / volume(A)
```

## Sample Output

```
=================================================================
  3D MODEL COMPARISON REPORT
  Reference (A) vs Generated (B)
=================================================================

──────────────────────────── VOLUMES ────────────────────────────
  Reference (A):          51,433.629
  Generated (B):          45,904.426
  Intersection (A∩B):     42,292.031
  Missing (A-B):           9,141.598  (17.8% of A)
  Extra (B-A):             3,612.395  (7.9% of B)

──────────────────────── PRIMARY METRICS ────────────────────────
  IoU (Jaccard):              0.7683  (1.0 = identical)
  Dice (F1):                  0.8690  (1.0 = identical)
  Precision:                  0.9213  (correctness of B)
  Recall:                     0.8223  (coverage of A)

──────────────────────── INTERPRETATION ─────────────────────────
  △ Partial match (IoU > 50%)
  → Under-generating: 17.8% of A is missing
  → Undersized by 10.8%
=================================================================
```