Skip to content

feat(geometry): tessellate IfcFaceBasedSurfaceModel - #59

Merged
Lutherwaves merged 4 commits into
mainfrom
feat/fbsm-dispatch
Sep 24, 2026
Merged

Lutherwaves merged 4 commits into
mainfrom
feat/fbsm-dispatch

Conversation

@Lutherwaves

Copy link
Copy Markdown
Member

tessellateItemDepth had no case for IfcFaceBasedSurfaceModel, so every
element built from one degraded to a bounding box. It was the top entry of the
parity harness's measured gap list at 65 occurrences, and in duplex_a it hid
235 IfcConnectedFaceSets that no other path could reach.

Measured result

Model OBB rate before after
ifcopenhouse 0.0% 0.0%
duplex_a 32.1% 1.9%
fzk_haus 2.4% 2.4%

duplex_a's row moves 146 Extrude + 0 Brep + 69 OBB + 3 Empty to
146 + 65 + 4 + 3. Exactly 65 elements moved from OBB to Brep, Extrude and
Empty did not move at all, and the IFCFACEBASEDSURFACEMODEL row left the
unhandled table. The model had zero brep elements before this.

This is far past the ~13% the design doc predicted, so it is worth saying
why.
That prediction came from an estimate that face-based surface models
accounted for ~58% of duplex_a's fallbacks — an estimate that conflated item
OCCURRENCES with fallback ELEMENTS, which is exactly the error
docs/coverage.md's own prose warns against. The measurement is the answer and
the estimate was the guess. The 65-occurrences/65-elements agreement is not a
coincidence either: occurrences are counted once per element that reaches an
item, so for a model with one such item per element the two figures are equal
by construction.

The residual 1.9% is four elements, and the only remaining entry in the
unhandled table is IFCPOLYGONALBOUNDEDHALFSPACE (11) — issue #52, the next PR.

Gate 1 is unchanged

Checked before regenerating anything, which is the order that matters:
make parity-baseline rewrites the file Gate 2 compares against, so running it
first would turn a failing gate green whether or not the change was correct.

331 elements compared, 329 contained, with exactly the two pre-existing
IfcStairFlight violations (1oKjKg9PD3fP1iIwXLh3lK min-y,
3KMJUyUe9DfQ2FOCd5ZoiN max-y) still present and still violating. No new
violation appeared, and parity/knownviolations.go needed no change. That
matters because this PR replaces conservative superset boxes with tight
meshes — the change most likely to expose an element that passed containment
only because its box was loose.

Shape of the change

FbsmFaces is attribute 0, a SET of IfcConnectedFaceSet. SbsmBoundary is
attribute 0, a SET of IfcShell. Different schema entities, identical
traversal, and brepMesh already tessellated a bare IfcConnectedFaceSet. So
the first commit generalises the existing shell-based helper into
surfaceModelMesh(m, attr) as a pure refactor, and the second adds the
dispatch case using it. The attribute is a parameter rather than assumed
because the two constants agreeing at 0 is a fact about the schema, not a rule.

Split that way so a bisect lands on the dispatch case rather than on the rename.

A note for whoever next touches brepMesh

surfaceModelMesh and brepMesh do not type-check set members — brepMesh
reads attribute 0 of whatever it is handed. The decline test documents this
honestly rather than claiming more than it checks: an IfcCartesianPoint gets
as far as its Coordinates list and declines only because that list holds
reals rather than references. A wrong-typed member whose attribute 0 were a
list of references would still be walked as a shell. Closing that would move
measured numbers, so it needs its own change with its own measurement pass.

🤖 Generated with Claude Code

@coderabbitai

coderabbitai Bot commented Sep 24, 2026 •

Copy link
Copy Markdown

Warning

Review limit reached

Next included review available in 48 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Advanced

Run ID: 0d1ccd42-d9af-490a-a023-555c2bc01e34

📥 Commits

Reviewing files that changed from the base of the PR and between 551ac66 and 7dd759c.

📒 Files selected for processing (7)
  • docs/coverage.md
  • geometry/brep.go
  • geometry/brep_test.go
  • geometry/geometry.go
  • geometry/testdata/synthetic/face_based_surface_model.ifc
  • geometry/unhandled.go
  • parity/testdata/baseline.json

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@codecov

codecov Bot commented Sep 24, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 66.66667% with 2 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
geometry/geometry.go 50.00% 2 Missing ⚠️

📢 Thoughts on this report? Let us know!

Lutherwaves and others added 4 commits September 24, 2026 14:17
IfcShellBasedSurfaceModel.SbsmBoundary and IfcFaceBasedSurfaceModel.FbsmFaces
are different schema entities with the same shape: attribute 0, a SET of
things brepMesh already tessellates. Generalise the shell-based helper so the
face-based dispatch case can reuse it rather than copy it.

No behaviour change.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
tessellateItemDepth had no case for it, so every element built from one
degraded to a bounding box. It is the top entry of the measured gap list at 65
occurrences, and in duplex_a it hides 235 IfcConnectedFaceSets that no other
path can reach.

FbsmFaces is a SET of IfcConnectedFaceSet, the same shape as the shell-based
model's SbsmBoundary, so the case reuses the helper rather than copying it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The "non-face-set member" subcase passed for an incidental reason: brepMesh
type-checks nothing, so an IfcCartesianPoint gets as far as its Coordinates
list and declines only because that list holds reals rather than references.
Rename and document it so it claims what it verifies. The duck-typing gap in
brepMesh is real and tracked separately; closing it would move measured
numbers and needs its own measurement pass.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Regenerated by `make parity-report` and `make parity-baseline` after the
dispatch case landed. Gate 2 fails in both directions by design, so a closed
gap has to be recorded rather than silently improving the numbers.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@Lutherwaves
Lutherwaves merged commit 30e4f54 into main Sep 24, 2026
13 checks passed
@Lutherwaves
Lutherwaves deleted the feat/fbsm-dispatch branch September 24, 2026 11:21
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