Skip to content

feat(ifc): an Area's space gets the region its boundary lines enclose, from its Level to the next storey (#763) (RE-317) - #1298

Merged
DrunkOnJava merged 17 commits into
mainfrom
feat/area-spaces-bodies
Nov 4, 2026
Merged

DrunkOnJava merged 17 commits into
mainfrom
feat/area-spaces-bodies

Conversation

@DrunkOnJava

@DrunkOnJava DrunkOnJava commented Nov 3, 2026 •

Copy link
Copy Markdown
Owner

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.

  • 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: fix(elements): Revit 2008 to 2023 frames with no ElementId of their own take the id of the declared record around them, and 2016 to 2023 leave out the elements outside the export phase and in a non-primary design option (#1261) (RE-315) #1286, 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, 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.
  • 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.
  • 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

  • README / docs reflect the state after this PR
  • Changelog fragment under changelog.d/added/
  • 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
  • Commit messages describe what and why; no emojis; no private paths
  • Reverse-engineering finding: probe under probes/examples/, the dated RE-317 report and the reconnaissance addendum

Legal and contribution hygiene

  • 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.

…ry, scored against Revit's IfcSpace profile (RE-317)
… around its point, from its Level to the next storey (#763) (RE-317)
…r an Area's body, with the RE-317 results (#763) (RE-317)
… the unmeasured non-story Level case (RE-317)
… read and placed, its Level and the Level above are both building stories, and no other Area has its region; the reason for each refusal is counted (RE-317)
…t figure in the space scorer's account, the unmeasured cases and the commands with their SHAs (RE-317)
…d lines are checked against the declared census, a line in a non-primary design option makes its group unreadable, and the free-end pruning is a worklist (RE-317)
…t the merge base, and the reproduce commands (RE-317)
…can returned, and the line groups are released before the regions are compared (#763) (RE-317)
…sources and counters beside the Area rows, refusals and area_body_refusals (#763) (RE-317)
@DrunkOnJava
DrunkOnJava marked this pull request as ready for review November 4, 2026 00:37
@DrunkOnJava
DrunkOnJava enabled auto-merge (squash) November 4, 2026 00:37
@DrunkOnJava
DrunkOnJava merged commit cab9452 into main Nov 4, 2026
28 checks passed
@DrunkOnJava
DrunkOnJava deleted the feat/area-spaces-bodies branch November 4, 2026 00:44
@DrunkOnJava

Copy link
Copy Markdown
Owner Author

Automated Claude Code review (the maintainer's review agent; it read the code, the report and the Measure runs, over three rounds). Round 1, two blocking items, fixed in 5a7e40a. The rule that every boundary line of the scheme on the Level is read was asserted, not checked: an Area is now drawn only when every boundary line the record chain declares is read and placed. The height also guessed in unmeasured cases: a body is now written only when the Area's Level and the Level above are both building stories. Round 2, no blocking items. Its findings were fixed in cdc21cf: an Area that shares its region with any placed Area gets no body; named lines are checked against the declared lines; a line in a non-primary design option makes its group unreadable; the pruning pass is linear. Round 3, no blocking items. Two findings were fixed in 6efadb4. The Level's Story Above setting is not read, and the docs list it as unmeasured.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant