Repository navigation
feat(rooms): a room whose stored solid the walk does not close takes the outline its solid's faces make, labelled stored_solid_faces or stored_solid_approximate (#90) (RE-316) - #1292
Conversation
…that does not close on its box (#1260) (RE-316)
…span and anchor and says which closes (#1260) (RE-316)
…ds and the plan edges at its floor (#1260) (RE-316)
…efore its first face (#1260) (RE-316)
…to the next solid's tag give the room's outline as the boundary of the region its directed plan edges wind around (#1260) (RE-316)
…th the walk on the rooms RE-101 kept (#1260) (RE-316)
…resolved, and a face outside the room's box rules the solid out (#1260) (RE-316)
…s are resolved, and a failed resolution says where the pieces do not link (#1260) (RE-316)
…ry has against the box (#1260) (RE-316)
…all 0.02 ft short of the record box (#1260) (RE-316)
…arries no prism marker, its volume being unmeasured (#1260) (RE-316)
…lving them gives against Revit's export, and what keeps its box (#1260) (RE-316)
…lved, and a solid with a round column is left to its box, since Revit's export does not always take either off (#1260) (RE-316)
… Revit's area and the rooms that keep their box (#1260) (RE-316)
… held-out Revit 2021 project's six rooms (#1260) (RE-316)
…m it does not close is resolved apart with its own profile source, and the work of a resolution is bounded (#1260) (RE-316)
…linder faces as RE-101 had it (#1260) (RE-316)
…nts and at a gap of 1024 bytes, and the work of a resolution is bounded and measured on crafted solids (#1260) (RE-316)
…nd labelled (#1260) (RE-316)
…d's list of objects, resolved rooms are labelled stored_solid_faces and stored_solid_approximate and counted, and the measured misses are stated (#1260) (RE-316)
…heir box (#1260) (RE-316)
… inside the walls and rooms around it, to tell a resolved outline Revit disagrees with (#1260) (RE-316)
…le its file is open (#1260) (RE-316)
…and every-face plan area, ours and Revit's, to test whether Revit's top surface is the room's plan (#1260) (RE-316)
…re one simple loop; a column subtracted or a shortfall makes it stored_solid_approximate (#1260) (RE-316)
… walk reads them, and stored_solid_approximate where a column overlaps a wall or the outline falls short (#1260) (RE-316)
…how far Revit's floor area is from them, and the scrub keeps the overlap scorer's counts (#1260) (RE-316)
…s do or do not close as the walk reads them (#1260) (RE-316)
…r, the scorer's upward faces read it and RE-313's 66% room as a fraction of themselves, the four guards were measured and none separates it, and the resolved outlines are labelled by how their edges close (#1260) (RE-316)
…ther edge, named everywhere; the span's looser join stated; the room schedule note and the held-out scrub of every-face lines corrected (#90) (RE-316)
…, from its Level to the next storey (#763) (RE-317) (#1298) ## Summary An Area's `IfcSpace` gets the region that the lines of its area scheme on its Level enclose around its point, its holes the loops directly inside, extruded from its Level up to the Level above it, as Revit's export draws it. On the BIM4LCA office all 30 Areas, as built and at building permit, equal Revit's in area (within 0.1%), outline (within 0.01 ft) and base and top (within 0.01 ft); no other export moved (RE-317). An Area whose boundary is not proven keeps no body and says why. ## Motivation Rooms and spaces are the first workflow made dependable (rooms first, #1260). The office's 30 Areas were written as `IfcSpace` with number, name and storey and no body (RE-191), the last 30 of Revit's 308 spaces rvt-rs did not draw. Refs #763. ## Changes - `probes/examples/probe_re317_area_boundaries.rs`, `tools/re/area_boundaries_vs_ifc.py`: where an Area's boundary lives (its scheme's `OST_AreaSchemeLines` elements, whose boxes are the lines; a point in its data object at byte 107) and how it maps to Revit's profile; each Level's Building Story setting. - `src/research/partition_area_boundaries.rs` (new): the planar arrangement of straight lines along the plan axes (splits at T joints and crossings, free ends left out, nested loops), capped at 2048 lines and 200,000 points, ring polygons built once, free ends pruned by a worklist (linear); `face_at`, the region around a point, fail closed. - `src/research/partition_area_records.rs`: the Area's point, scheme, named lines, Level and the Level above. The fail-closed contract, after review: - every declared area boundary line of the file must have been returned by the scan or recorded as a stray; one that was not, or whose height is not a number, may belong to any group, so no Area of the file is drawn; - a line whose copies disagree taints both copies' Levels; - the Area's Level and the Level above are both read as building stories, with no other Level between them or at either elevation, or the Area has no body (the Level's story above is not read); - an Area whose region another placed Area of its scheme and Level shares gets none, whatever stops that Area from being drawn (every Area with a point and a scheme and Level gets its region first); - the lines an Area's record names are checked against the declared census, so a named line that is conflicted, unkeyed or in a non-primary option refuses it (`named_lines_not_in_scheme_and_level`); - a line in a non-primary design option makes its group unreadable instead of being skipped (no such line exists in the office or the 2024 samples, so Revit's handling is not measured); - line ids below 1000 are not read as an Area's named lines; - the reason for each refusal is `RvtElementRecordGeometry.AreaBodyRefusal` and is counted in `exported.area_body_refusals` (schema and docs updated). - `src/ifc/export_content.rs`: the body, `SpaceOutlineSource` `area_boundary`, `BodySource` / `ProfileSource` `partition_area_boundary_lines`, `BodyHeightSource` `area_level_to_next_storey`; an Area is left out of the spaces that hold a room's furniture; the room schedule gives an Area its area. - `docs/export-diagnostics.md` and the schema: `area_body_refusals` is empty when every Area is drawn or the export has no geometry; `status.md` names it; `evidence.md` wording fixed; the report has the reproduce command for the samples probe. - `tools/re/space_profiles_vs_ifc.py` counts Areas as their own kind and scores the lowest and highest vertex of each body against Revit's (`plan_profiles_vs_ifc.py` can return the heights). - Docs: `docs/rooms.md` (what is not measured now names the top storey, non-story Levels, the story above, Areas bounded partly by walls and the other releases), `export-diagnostics.md`, `status.md`, `evidence.md`, `supported-profile.md`, `support-matrix.json`, `user-guide.md`, `format-reconnaissance.md`, `reports/element-framing/RE-317-area-boundaries.md`, changelog fragment. ## Testing Unit tests are banned: end to end on real files only. No local build; GitHub's runners did all of it. - [x] Verified end to end on real Revit files, measured against Revit's own export, with the scorer and probe that reproduce it (commands with SHAs in the report). The branch has `main` merged in (37816e3: #1286, #1292, the door widths), so the numbers below are against `main` as it now is: - Measure unfiltered on the merged head, `-f base=main -f nordic=true -f r2025=true -f samples=true -f holdout=true`, every scorer, run 41740909229. `space_profiles_vs_ifc` on the BIM4LCA office as built and at permit: spaces without a body 30 -> 0; "rvt-rs area area boundary: 30, area within 0.1% 30, outline within 0.01 ft 30, both 30, boundary within 0.01 ft 30" and its height line "30, lowest and highest vertex within 0.01 ft 30" on each file; spaces in both 276 -> 306 and 278 -> 308; area within 0.1% 230 -> 260 and 232 -> 262, outline 220 -> 250 and 225 -> 255; every space's base and top within 0.01 ft (306 of 306 and 308 of 308); the room and family-instance lines are the ones `main` prints. - Of 111 models and samples (every release 2015 to 2027), the IFC hash moved on four: the two office files and the two 2024 samples with Areas. The compare job's scorer diff is 68 lines: the space lines, and in `schema.txt` the 30 (office) and 3 (`rac_advanced`) new swept solids with a Position. Property sets, quantities, plan profiles, class sets, room names and storeys, and the WHERE rules print the same. No held-out model is in the scorer diff: every held-out `space profiles:` count line is identical to `main`'s (BIM Whale 2021: 0 of Revit's 8 written, as before). - The office's Levels are all building stories (probe, run 41721498984); the probe's point check: the point lies in Revit's profile on 30 of 30 on both files (run 41713925390). - Autodesk's samples (no Revit export to score): `rac_advanced` 3 of 25 Areas gain a body, 18 are refused as `boundary_lines_not_readable` (a slanted line or arc in their group) and 4 as `point_in_no_single_region` (their point lies in no loop of the lines; possibly Areas bounded partly by walls, the main gap, not examined further); `rst_basic`'s one Area is refused as `next_storey_not_proven`; every other sample is byte-identical. - [x] CI: fmt, clippy, rustdoc, MSRV 1.87, i686, IfcOpenShell validates exports (Core Interior 116 of 116, RE1 11 of 11), Corpus health, PII guard on the final head; the merge resolved nine conflicts keeping both sides (main's room outline sources and counters beside the Area rows, refusals and `area_body_refusals`; schedule rooms take their area per main's rule, Areas per this change's). `tools/check-local.sh` was not run: no local builds here. - [x] IFC exporter touched; the changed observations: none of the witness reference models changed (Core Interior, RE1 byte-identical), so no witness refresh. ## Audit-honesty checklist - [x] README / docs reflect the state after this PR - [x] Changelog fragment under `changelog.d/added/` - [x] The limitations are documented in the PR body, in `docs/rooms.md` "What is not measured" and in the report: the top storey, Levels that are not building stories, the story above (never read), Areas bounded partly by walls, boundary lines that are not along the plan axes, the Area's base offset, releases other than 2024, and `Qto_SpaceBaseQuantities.Height`, which Revit writes on an Area and rvt-rs does not yet - [x] Commit messages describe what and why; no emojis; no private paths - [x] Reverse-engineering finding: probe under `probes/examples/`, the dated RE-317 report and the reconnaissance addendum ## Legal and contribution hygiene - [x] Apache-2.0; no Autodesk proprietary source (revit-ifc read for what Revit writes, `SpatialElementExporter.GetScaledHeight` and `LevelUtil.CalculateDistanceToNextLevel`, cited and not copied); no fixtures added. ## Breaking changes None to the stable API. `partition_area_records::areas_from_partition_chain_records` (behind `unstable-research`) takes the recovered Levels as a fourth argument; `ExportedModelDiagnostics` gains `area_body_refusals` (serde default). ## Screenshots or output samples The Measure counts above. An Area now reads `SpaceOutlineSource` `area_boundary`, `BodySource` `partition_area_boundary_lines`, `ProfileVertexCount`, `ProfileVoidCount` and `BodyHeightSource` `area_level_to_next_storey`; an Area without a body reads `AreaBodyRefusal`. ## Related issues Refs #1194 (its Areas item: Revit 2024 Areas now get a body; Areas on other releases, slanted and arc boundary lines, and Story Above stay open), #1260 (rooms first). #763 (closed) was the earlier family-space work on the same office.
|
Automated Claude Code review (the maintainer's review agent; it read the code, the scorers and the Measure runs, over three rounds). Round 1: the PR body held another PR's text, a solid's faces had no bound and no cap against hostile input, and the column-overlap slack had no label of its own; fixed. Round 2: no blocking item after a check that office room 1671995, which had read 323% over Revit's by its upward faces, is 0.45% under Revit's floor area (Revit's ceiling over it is not flat); the labels were narrowed so only rooms whose edges close as the walk reads them are |
Summary
A room whose stored solid RE-101's exact walk does not close on its record box is read again from the faces its solid lists, up to the solid's own end, and labelled apart in
SpaceOutlineSource:stored_solid_faceswhere the faces' edges close as the walk reads them,stored_solid_approximatewhere a column overlaps a wall (its area depends on whether Revit's export takes the column off) or the outline falls up to 0.02 ft short of the box (Griffin, 2026-11-03: close them, labelled).stored_solidstays the exact walk's. On the BIM4LCA office as built all 13 rooms that kept their box take an outline (9stored_solid_faces, 4stored_solid_approximate); at building permit 11 of 12 do (allstored_solid_approximate).Motivation
Rooms first (#1260): a room written as its record box where it is not a rectangle is the failure that matters. RE-313 left 12 rooms per office file on their box with the cause not found; RE-316 finds it (a record longer than 0x200 bytes between two faces, walls on lines up to 0.0015 ft apart, a column overlapping a wall, a solid longer than the 64 KB window).
Round 3 of the review found room 1671995 at 323% over Revit's area (350% at permit) and asked for a guard. The 323% is the scorer's: it compares the faces of Revit's mesh that point up, and Revit's ceiling over that room is mostly not flat (1218.33 ft² in 9 patches). Revit's floor under it is 5177.37 ft² and ours 5154.12, 0.45% under; the same metric reads RE-313's 66% room (2611271) as 66% off where its floor agrees to 0.1%. See RE-316 section 3.
Changes
src/research/partition_room_boundaries.rs: the span reads its edges two ways. As the walk reads them (stored_solid_faces: one outer loop, nested loops, no crossing, and a span bounded by the solid's list of objects and a gap of 1,024 bytes) or as the boundary of the region they wind around (stored_solid_approximate). Column-involved rooms are no longer labelled as the exact reading.tools/re/space_overlaps_vs_ifc.py(new scorer, registered in Measure's IFC scorers; its count lines are kept for held-out models bytools/ci/scrub-holdout-results.sh): each space's upward-face, floor and every-face plan areas against Revit's, and how much of a room lies inside the walls and rooms around it.docs/rooms.md,docs/export-diagnostics.md(+ schema),docs/status.md,docs/supported-profile.md,docs/evidence.md,changelog.d/changed/1260-room-solids-resolved.md,reports/element-framing/RE-316-room-solids-resolved.md; the closure probe says why each resolved room's edges do or do not close.Testing
Unit tests are banned in this repository: none added. End to end on Measure (commands in RE-316 section 6).
tools/check-local.shpasses: not run on this Mac (no local compute); CI's fmt, clippy, doc, MSRV, real-file and IfcOpenShell jobs ran green on this branchspace_profiles_vs_ifcandspace_overlaps_vs_ifc, room by room in RE-316 section 3; Core Interior and RE1 Architecture unchanged (116 of 116, 11 of 11); a held-out Revit 2021 project, counts onlymain: 41728355227 (-f base=main -f nordic=true -f r2025=true -f samples=true -f holdout=true, head a00e705, base 7fd339e): scorer output differs on five files, the exports' hashes on the office, the held-out project and onermeroom per file; nothing elseRooms per outline source (export diagnostics):
stored_solidstored_solid_facesstored_solid_approximaterecord_boxArea within 0.1% of Revit's export, the scorer's upward faces (before to after): office as built 221 to 230, at permit 222 to 232, Warmond (held out, counts only) 174 to 179 of 179; Core Interior and RE1 unchanged. By Revit's floor:
stored_solid221 of 221 and 222 of 222;stored_solid_faces9 of 9 (and 6 of 6 on Warmond);stored_solid_approximate0 of 4 as built (0.45% to 0.68% under) and 10 of 11 at permit (1671995, 0.45% under).The review's four guards (wall overlap, shell closure, volume, containment) were measured on every room the span reads. None rejects 1671995 and keeps the rooms that agree: it covers 0.03% of itself with walls (the agreeing rooms 0.10% to 7.59%, the walk's rooms up to 23%) and 0.00% of other rooms (0.00% on every room); a closed shell and the volume need the floor and ceiling faces, which
face_atreads on 3 of the 13 rooms as built; containment holds on all. No guard is applied, no room is refused for a guard's reason. The residual (a closed loop of another element's faces inside the box and within 1,024 bytes of the last listed face) is not closed by these either; a foreign face that crosses a wall makes the roomstored_solid_approximate.Where this differs from the round-3 instruction: the fallback (withhold the faces reading) would turn 1671995 and the 9 exact rooms back into boxes, which are 1.0% over Revit's floor for 1671995 and 3% to 67% over for the nine. Instead the exact label is narrowed to rooms whose edges close as the walk reads them, and every column-involved room is
stored_solid_approximatewith its measured error. Withholding it is a small change toresolved_outline_of_solidsif the coordinator wants it.Audit-honesty checklist
changelog.d/changed/1260-room-solids-resolved.mdrac_advancedrooms with round columns, 49 of 50rmerooms, Snowdon Towers' seven and room 3095831 keep their boxprobes/examples/andtools/re/, and a dated reportLegal and contribution hygiene
Breaking changes
None to the API outside
rvt::research.SpaceOutlineSourcegains two values; an importer that matchedstored_solidexactly now seesstored_solid_facesandstored_solid_approximateon rooms that wererecord_boxbefore.Screenshots or output samples
IFC output changes on 13 + 11 office rooms, the held-out project's 6 and one
rmeroom per file: theirIfcSpacebody is the outline where it was the box. Room by room in RE-316 section 3.Related issues
Refs #90 (rooms still drawn as their box: this resolves 13 office rooms as built and 12 at permit, and lists those that keep it: office 3095831 at permit, six
rac_advancedrooms with round columns, 49 of 50rmerooms, Snowdon's seven). #1260 (closed by #1288) is the rooms-first report that started this.