Boxes to Walls: how PlanLint grounds a model's guess
A vision model points at every door, window and room and draws a rough box around it. Snapping is the deterministic step that moves that rough box onto the drawing's real geometry, so the size that reaches a code check is measured, not guessed. Here is how it's done, worked on one sheet.
- Example
- Living Room
- Sheet
- 1st Floor Plan
- Scale
- 1/4" = 1'-0"
- Paths
- vector · raster
The VLM's boxes are approximate on purpose. Its job is to say what is on the page and roughly where. Snapping takes that label and finds the exact extents in the geometry underneath: the wall a room is bounded by, the gap a door cuts through a wall. If the geometry can't confirm a size, the asset is left unmeasured and marked for review, never handed a made-up number.
AVector drawings
A born-digital CAD PDF carries exact line-work, so geometry is the ground truth: pure math over the primitives, no model. This is the high-confidence path.
Read the walls
Every line and rectangle edge is pulled from the PDF and the long, straight, axis-aligned ones are
grouped into wall runs (blue). Segments that end inside
a text label, such as dimension lines and callout leaders, are dropped. What survives are wall
candidates: a counter edge or a run of porch decking is long and straight
too. The coverage, corner and flanked-gap tests in the next steps are what settle
which of them are really walls.
→ extract_primitives, build_wall_runs
Snap the room to its inner walls
Each edge of the amber box moves to the nearest wall run that covers most of the room's width or height
using a coverage test rather than a center-crossing one, so a doorway sitting at the room's midline doesn't defeat
it. The edge is then refined onto the wall's room-facing inner face using the
corners where perpendicular walls meet, which also rejects any
stray line that isn't part of a real corner. The green box traces the interior exactly.
→ snap_room_box
Ground an opening in the wall gap
A door or window is the gap in a wall run, flanked by wall on both sides. The box is rebuilt as
that gap span × the wall's thickness, and the gap length, read straight from the geometry, is the
clear width (red). Here that is 24".
Guards keep a door from swallowing a whole wall; a solid (hatched) shape filling the box marks a pier,
not an opening, and is dropped. → classify_opening_vector
BScanned drawings
When a sheet is a scan (or a flattened PDF with no usable line-work), geometry comes from the pixels instead: deterministic OpenCV, still no model.
Build a wall mask
The rendered page is thresholded to ink, hatched and double-line walls are consolidated into solid
bands, and only strokes that are both thick and long survive, so text and symbols fall away. The
opening kernel is capped so thin interior partitions aren't erased along with the noise.
→ analyze_page_image
Reconcile the room box, edge by edge
A flood fill grows from the box center through free space (openings sealed first) to bound the room and
measure its interior area. Its box is then reconciled with the VLM box per edge: whichever
edge a wall actually backs in the mask is kept. On this room the flood fill nails the top and bottom
(fill), and the VLM holds the left and right
(VLM) where the fill would have leaked out a doorway, no wall
backs that edge, so the VLM edge wins. Every side lands on its wall.
→ snap_room, reconcile_room_box
Pixels are a weaker witness, and the system says so
A hatched pier and a densely-drawn window can look identical in a mask, so an ambiguous raster opening is reported as informational and never allowed to fail a check. That is why a vector-snapped result outranks a raster-snapped one below.
every measurement is labelled
Provenance sets the confidence
A value carries where it came from, and a stronger witness outranks a weaker one. A
size nothing can confirm becomes NEEDS_REVIEW, never a fabricated number.
The one line the whole design protects
Snapping turns a model's approximation into a grounded measurement, or holds it back for a human. The VLM reasons about which feature is where; the geometry proves how big it is; and a pure-Python checker compares that measurement to the code. No model output ever flows straight into a verdict.
Generated from the American Farmhouse set (CC-BY-SA, freefarmhouse.com) · PlanLint · figures rendered by
the live vector_geometry / raster_geometry functions.