Honest starting point
One-Click Area is good and we want it to be great. Today it works like this (web/src/lib/oneclick.ts, ~550 lines, pure TS):
extractVectorGeometry pulls the PDF's real linework with meta bits (curve chords, clip-only paths, filled-not-stroked poché outlines).
classifyHatchSegs decides which segments are hatch (tile patterns, poché) vs. walls, using evenly-spaced-parallel-row heuristics (HATCH_MIN_RUN, pitch regularity, span/width protection ratios).
buildMask rasterizes the boundary segments into a ≤3000 px 1-bit mask; floodRegion grows from your click, with an escalating strategy: strict fill first, then a grow-but-verify pass that drops soft (hatch) boundaries when the region reads as predominantly hatch-bounded — rejected if it leaks (>30% of sheet), stays tiny, or balloons past HATCH_GROWTH_MAX.
- Scanned plans skip vector entirely:
rastermask.ts (Bradley adaptive threshold) builds the mask from pixels.
That architecture has survived real plans (VA medical center, hotel work) through several hardening rounds — fillOnly poché walls, span protection, the escalation band. But it has structural ceilings, and this issue names them plainly.
Where it breaks (all reproducible)
- Dense fine-pitch hatch still wins sometimes. Small rooms fully lined with tight tile grids (the bundled demo plan's toilet rooms at certain zooms) land the seed inside linework denser than the mask can separate →
tiny refusal. The escalation band helps; the class of failure remains. The guard is honest (it refuses rather than guessing) — but a refusal is still a manual trace.
- Boundary gaps leak. Plans that draw wall openings without door swings (curve chords are what usually close doorways), dashed demising walls, or corridors open to lobbies → the flood escapes and aborts at
LEAK_FRACTION, or worse, returns a plausible-looking over-region. There is no gap-closing tolerance today.
- It's raster underneath. Vector truth gets rasterized to a capped mask, so results are resolution-dependent at the margins: hairline walls on E-size sheets alias, and two rooms separated by a 1-mask-px wall can bleed at the cap.
- A wall of tuned constants.
HATCH_*, SPAN_PROTECT_RATIO, TINY_PX, escalation fractions — each calibrated against plans we've seen. Every new drafting style risks needing a new constant, and nothing measures whether a constant change helps globally or just locally. This is the deepest issue: the engine can't currently prove it got better.
- No confidence output. A barely-passed escalation and a clean strict fill look identical to the user (and to the capture layer, beyond
hatch_filtered/raster_traced flags). The engine knows things it doesn't say: which tier fired, the hatch-bounded fraction, the growth ratio.
- Scanned-plan path is basic. One global adaptive threshold; skewed, speckled, or low-contrast scans degrade fast, and there's no deskew or despeckle pass.
- One room per click. Fine for a wing; tedious for "every patient room on three floors."
Improvement directions (ranked by leverage)
A. Vector-native face extraction (the big one). For vector plans, stop rasterizing: build a planar subdivision of the classified wall segments and extract faces — every enclosed face IS a room, exact to the linework, resolution-independent, and all rooms come out at once (which also unlocks batch fill). Hard parts are real: segment noise, T-junctions, gap tolerance, curves. A credible path keeps the flood engine as cross-check and fallback: face extraction proposes, flood verifies. This is a meaty computational-geometry project and the single highest-leverage change available.
B. Explicit gap-closing tolerance. Bridge sub-tolerance endpoint gaps (door openings, sloppy linework) before mask/face building — with the bridged gaps reported, not silent, so a fill can say "closed 2 openings ≤ 3′". Fixes failure mode 2 honestly.
C. Hatch classification by periodicity, not rows. Replace/augment the parallel-row heuristics with autocorrelation or (angle, pitch, pen-width) clustering to find periodic families directly — robust to two-direction tile grids and rotated patterns, and it should reduce the constant count, not grow it.
D. Confidence + receipts. Expose what the engine already knows per trace: tier used, hatch-bounded fraction, growth ratio, gap bridges → a 0–1 confidence surfaced in the UI (and available to provenance). Cheap, immediately useful, and prerequisite for any batch mode.
E. The benchmark corpus (prerequisite for everything above). A committed fixture set of plan pages — public-domain or publicly-released sets with an architect's seal only, metadata stripped, per repo policy — with golden room polygons, scored in CI by IoU. No engine PR is reviewable without corpus results: mean/floor IoU, refusal rate, leak rate, per-fixture deltas. This converts "my flood fill isn't the best" from a feeling into a number that must go up.
F. Batch fill. "Fill all rooms like this one" — seeded from the text layer's room tags or from face extraction (A). Only safe on top of D's confidence.
Ground rules for contributions
oneclick.ts / rastermask.ts stay pure (no React, no DOM) — that's what makes them testable and reusable (the MCP server runs the same engine headlessly).
- The engine must stay honest: refusal over guessing. Any change that trades silent wrongness for coverage will be rejected regardless of its IoU mean.
- Perf budget: mask/face build must stay interactive on E-size sheets (document timings in the PR).
- Existing regression tests (e.g., the poché-wall case) must keep passing; new failure modes fixed = new named regression fixtures added.
On ordering
The corpus (E) is not the warm-up item, it is the instrument. Every other direction on this list is a claim about accuracy, and a claim about accuracy without a corpus is a preference. Corpus work is in progress — coordinate here before starting a parallel one.
Face extraction (A) is the item with the highest ceiling and the longest tail of hard cases. It is a genuine computational-geometry project: planar subdivision over noisy segments with T-junctions, tolerance-based gap closure, and curve handling, with the existing flood engine kept as cross-check rather than replaced. Nobody should start it without the corpus in place, because there is no other way to know whether it worked.
Honest starting point
One-Click Area is good and we want it to be great. Today it works like this (
web/src/lib/oneclick.ts, ~550 lines, pure TS):extractVectorGeometrypulls the PDF's real linework with meta bits (curve chords, clip-only paths, filled-not-stroked poché outlines).classifyHatchSegsdecides which segments are hatch (tile patterns, poché) vs. walls, using evenly-spaced-parallel-row heuristics (HATCH_MIN_RUN, pitch regularity, span/width protection ratios).buildMaskrasterizes the boundary segments into a ≤3000 px 1-bit mask;floodRegiongrows from your click, with an escalating strategy: strict fill first, then a grow-but-verify pass that drops soft (hatch) boundaries when the region reads as predominantly hatch-bounded — rejected if it leaks (>30% of sheet), stays tiny, or balloons pastHATCH_GROWTH_MAX.rastermask.ts(Bradley adaptive threshold) builds the mask from pixels.That architecture has survived real plans (VA medical center, hotel work) through several hardening rounds — fillOnly poché walls, span protection, the escalation band. But it has structural ceilings, and this issue names them plainly.
Where it breaks (all reproducible)
tinyrefusal. The escalation band helps; the class of failure remains. The guard is honest (it refuses rather than guessing) — but a refusal is still a manual trace.LEAK_FRACTION, or worse, returns a plausible-looking over-region. There is no gap-closing tolerance today.HATCH_*,SPAN_PROTECT_RATIO,TINY_PX, escalation fractions — each calibrated against plans we've seen. Every new drafting style risks needing a new constant, and nothing measures whether a constant change helps globally or just locally. This is the deepest issue: the engine can't currently prove it got better.hatch_filtered/raster_tracedflags). The engine knows things it doesn't say: which tier fired, the hatch-bounded fraction, the growth ratio.Improvement directions (ranked by leverage)
A. Vector-native face extraction (the big one). For vector plans, stop rasterizing: build a planar subdivision of the classified wall segments and extract faces — every enclosed face IS a room, exact to the linework, resolution-independent, and all rooms come out at once (which also unlocks batch fill). Hard parts are real: segment noise, T-junctions, gap tolerance, curves. A credible path keeps the flood engine as cross-check and fallback: face extraction proposes, flood verifies. This is a meaty computational-geometry project and the single highest-leverage change available.
B. Explicit gap-closing tolerance. Bridge sub-tolerance endpoint gaps (door openings, sloppy linework) before mask/face building — with the bridged gaps reported, not silent, so a fill can say "closed 2 openings ≤ 3′". Fixes failure mode 2 honestly.
C. Hatch classification by periodicity, not rows. Replace/augment the parallel-row heuristics with autocorrelation or (angle, pitch, pen-width) clustering to find periodic families directly — robust to two-direction tile grids and rotated patterns, and it should reduce the constant count, not grow it.
D. Confidence + receipts. Expose what the engine already knows per trace: tier used, hatch-bounded fraction, growth ratio, gap bridges → a 0–1 confidence surfaced in the UI (and available to provenance). Cheap, immediately useful, and prerequisite for any batch mode.
E. The benchmark corpus (prerequisite for everything above). A committed fixture set of plan pages — public-domain or publicly-released sets with an architect's seal only, metadata stripped, per repo policy — with golden room polygons, scored in CI by IoU. No engine PR is reviewable without corpus results: mean/floor IoU, refusal rate, leak rate, per-fixture deltas. This converts "my flood fill isn't the best" from a feeling into a number that must go up.
F. Batch fill. "Fill all rooms like this one" — seeded from the text layer's room tags or from face extraction (A). Only safe on top of D's confidence.
Ground rules for contributions
oneclick.ts/rastermask.tsstay pure (no React, no DOM) — that's what makes them testable and reusable (the MCP server runs the same engine headlessly).On ordering
The corpus (E) is not the warm-up item, it is the instrument. Every other direction on this list is a claim about accuracy, and a claim about accuracy without a corpus is a preference. Corpus work is in progress — coordinate here before starting a parallel one.
Face extraction (A) is the item with the highest ceiling and the longest tail of hard cases. It is a genuine computational-geometry project: planar subdivision over noisy segments with T-junctions, tolerance-based gap closure, and curve handling, with the existing flood engine kept as cross-check rather than replaced. Nobody should start it without the corpus in place, because there is no other way to know whether it worked.