Skip to main content

Geometry Repair

Module: src-tauri/src/pipeline/repair.rs Function: repair_scene(scene: &mut Value) -> Vec<String>

The repair stage automatically fixes common geometry issues in the VLM output before validation. It operates on the HomeSceneJSON in meter coordinates and returns a log of all repair actions taken.

Repair Operations

Room Polygon Repairs

OperationDescriptionThresholdImplementation
Degenerate removalRemove rooms with area < 0.5 m^2MIN_ROOM_AREA = 0.5rooms.retain()
Vertex snappingSnap nearby vertices to shared coordinatesSNAP_THRESHOLD = 0.05 (5cm)Grid-based snap map
OrthogonalizationAlign near-H/V edges to exact H/VORTHO_THRESHOLD_DEG = 10.0Edge angle analysis
Closure repairAppend first point if polygon unclosed1mm gap toleranceDistance check
Overlap detectionDetect and flag room polygon overlaps50% vertex containmentPoint-in-polygon test

Vertex Snapping

Builds a spatial grid from all polygon vertices across all rooms. Vertices within 5cm of each other are snapped to their average position:

fn build_snap_map(points, threshold: 0.05)
// Grid cell size = threshold * 1000 (5cm -> 50 units)
// Points in the same cell -> snap to centroid

This ensures shared walls between adjacent rooms have exactly matching coordinates.

Orthogonalization

For each polygon edge, checks if the angle is within 10 degrees of horizontal or vertical. If any edge needs correction, snaps all edges:

  • If |dx| > |dz|: snap to horizontal (keep X, set Z to start's Z)
  • If |dz| >= |dx|: snap to vertical (keep Z, set X to start's X)

Closure Repair

Checks if the first and last polygon points are more than 1mm apart. If so, appends a copy of the first point.

Overlap Detection

For each pair of rooms, checks if more than 50% of one room's vertices fall inside the other room's polygon. Overlaps are flagged but not auto-resolved (may need manual review).

Wall Repairs

OperationDescriptionThresholdImplementation
Endpoint snappingSnap nearby wall endpoints to shared coordinatesWALL_SNAP_THRESHOLD = 0.05 (5cm)Same grid-based snap map
Collinear mergingMerge collinear and nearby wall segmentsangle < 5 deg, dist < 10cmProjection-based merge
room_refs repairFix room_refs based on wall-polygon edge matchingmidpoint dist < 30cm, length ratio > 0.5Edge proximity check

Collinear Merging

Two walls can be merged if:

  1. Angle difference < 5 degrees (COLLINEAR_ANGLE_THRESHOLD_DEG)
  2. One wall's endpoint is within 10cm of the other wall's line (COLLINEAR_DIST_THRESHOLD)

The merged wall spans the two extreme endpoints along the common direction:

fn merge_collinear_walls(walls) {
// For each pair (i, j):
// 1. Check angle similarity
// 2. Check point-to-segment distance
// 3. Project all 4 endpoints onto the common direction
// 4. Take the two extreme points as the new wall
// 5. Mark j for removal
}

room_refs Repair

For each wall, finds rooms whose polygon has an edge close to the wall:

fn fix_wall_room_refs(wall, rooms) -> bool

For each room polygon edge:

  1. Compute the wall midpoint and edge midpoint
  2. Compute midpoint distance
  3. Compute length ratio (shorter/longer)
  4. If midpoint distance < 30cm AND length ratio > 0.5, the room is a match

If the matching rooms differ from the current room_refs, updates the wall.

Opening Repairs

OperationDescriptionThreshold
RebindingRebind openings to nearest wallwall_thickness * 2

For each door and window:

  1. Find the nearest wall using point-to-segment distance
  2. If the nearest wall differs from the current wall_ref AND distance <= wall_thickness * 2, rebind

Constants Summary

ConstantValueUsed By
SNAP_THRESHOLD0.05m (5cm)Room vertex snapping
ORTHO_THRESHOLD_DEG10.0 degEdge orthogonalization
MIN_ROOM_AREA0.5 m^2Degenerate room removal
WALL_SNAP_THRESHOLD0.05m (5cm)Wall endpoint snapping
COLLINEAR_DIST_THRESHOLD0.1m (10cm)Collinear wall merging
COLLINEAR_ANGLE_THRESHOLD_DEG5.0 degCollinear wall merging

Repair Log Output

Returns a Vec<String> of human-readable repair actions:

removed 1 degenerate room(s) with area < 0.5 m^2
snapped 18 polygon vertex/vertices to nearby points
orthogonalized 3 room polygon(s)
closed 1 unclosed polygon(s)
detected 1 room overlap(s) -- may need manual review
snapped 6 wall endpoint(s)
merged 2 collinear wall segment(s)
fixed room_refs for 4 wall(s)
rebound 1 opening(s) to closer wall

Saved to data/pipeline/{project_id}/repair_log.json.

Implementation Details

Spatial Grid for Snapping

The snap map uses a grid-based spatial hash for efficient neighbor lookup:

fn build_snap_map(points, threshold) -> HashMap<(i64, i64), [f64; 2]> {
let grid = (threshold * 1000.0) as i64; // 5cm -> 50
// Each point maps to a cell: (x*1000/grid, y*1000/grid)
// Points in the same cell -> snap to their centroid
}

Point-in-Polygon Test

Uses the ray-casting algorithm for overlap detection:

fn point_in_polygon(point, polygon) -> bool {
// Cast a ray from the point and count edge crossings
// Odd crossings = inside, even = outside
}

Ordering

Repairs run in a fixed order: rooms first, then walls, then openings. This ensures that wall repairs can reference the already-repaired room polygons, and opening repairs can reference repaired walls.

When to Expect Repairs

VLM QualityExpected Repairs
High (clear walls, good labels)Few vertex snaps, maybe 1-2 merges
Medium (some ambiguity)Vertex snaps, orthogonalization, closure fixes
Low (faint walls, guessing)Many snaps, degenerate removals, overlap warnings