Skip to content

Commit 5439cce

Browse files
authored
Render IfcExtrudedAreaSolidTapered (tapered extrusions) (#630)
* geometry: render IfcExtrudedAreaSolidTapered (#628) Tapered extrusions were skipped by the geometry engine even though the parser already recognised the entity. Add a dedicated processor that lofts between SweptArea and EndSweptArea, register it on the router, and accept the subtype in the profile extractor and IfcMappedItem dispatcher so the elements show up in 2D projection too. Falls back to a uniform extrusion when EndSweptArea is missing so malformed files still render. * geometry: address review feedback on tapered extrusion (#630) - Don't route IfcExtrudedAreaSolidTapered through the constant-profile 2D drawing extractor. extract_extruded_solid only stores SweptArea, so emitting tapered solids there silently under-reports footprints. Tracked as a follow-up; the 3D path still renders them. - Reject zero-length ExtrudedDirection vectors instead of normalizing to NaNs, which would propagate into the transform and mesh. - Treat EndSweptArea profile-processing errors as a fallback to uniform extrusion (matching the existing null/empty handling) rather than aborting the whole solid. - Build filtered Profile2D copies so cap triangulation only includes the hole pairs we actually loft. With the previous code, an extra hole on one profile would leave an opening in the cap with no matching side wall, producing a non-manifold mesh. Regenerates packages/wasm/pkg via scripts/build-wasm.sh so the Vercel preview and downstream packages pick up the geometry fix. * geometry: fix stale doc on extrude_profile_lofted hole handling The previous wording said mismatched-count hole pairs were dropped; the implementation actually resamples them via match_loop_lengths, only dropping pairs where one side has fewer than 3 vertices. * geometry: fix inverted side-wall normals on tapered extrusions `edge_a.cross(edge_b)` collapses to the negative of the normal that `create_side_walls` produces in the untapered limit, so tapered faces shaded inward relative to regular extrusions. Switch to `edge_b.cross(edge_a)` (tangent rotated +90° CCW around +Z, matching the existing convention) and document the assumption. Adds a regression test that lofts identical profiles and asserts the side-wall normal direction agrees with `extrude_profile`. Also documents in the doc comment that we trust IFC WR2 to mean shared winding direction and corresponding start vertex; orientation detection / cyclic offset alignment is intentionally not added (would be defensive coding for malformed input that the spec forbids). Two related CodeRabbit comments not addressed in this commit: - README absolute-link suggestion targets pre-existing language in the root README, unrelated to #628. - Winding/cyclic-offset alignment in match_loop_lengths is the defensive coding above. Regenerates packages/wasm/pkg
1 parent 94b4df3 commit 5439cce

16 files changed

Lines changed: 630 additions & 34 deletions

File tree

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
---
2+
"@ifc-lite/geometry": patch
3+
---
4+
5+
Render `IfcExtrudedAreaSolidTapered` (issue #628).
6+
7+
Tapered extrusions (e.g. beams or columns whose cross-section transitions
8+
between a `SweptArea` profile at the base and an `EndSweptArea` profile at
9+
`Depth`) were recognised by the parser but silently skipped by the geometry
10+
engine, so the elements never appeared in the viewer.
11+
12+
The Rust geometry crate now ships:
13+
14+
- `extrude_profile_lofted` in `extrusion.rs` — generates caps from each
15+
profile's own triangulation and stitches the side walls 1:1, resampling
16+
the shorter outer loop by arc length when authoring tools emit profiles
17+
with mismatched vertex counts. Side normals are computed from the actual
18+
3D quad so sloped faces shade correctly.
19+
- `ExtrudedAreaSolidTaperedProcessor` registered alongside the existing
20+
`ExtrudedAreaSolidProcessor`. Falls back to a uniform extrusion if
21+
`EndSweptArea` is missing so malformed files still render.
22+
- `IfcExtrudedAreaSolidTapered` is now accepted by `profile_extractor`
23+
(used by 2D drawing projection) and the `IfcMappedItem` dispatcher.
24+
25+
Out of scope for this patch and called out for follow-up:
26+
`IfcRevolvedAreaSolidTapered`, plus tapered solids participating in
27+
`IfcBooleanClippingResult` / openings / material-layer slicing.

Cargo.lock

Lines changed: 6 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/wasm/pkg/README.md

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ Open, view, and work with IFC files. Right in the browser.
2929

3030
Parse, view, query, edit, and export IFC files in the browser. Rust + WASM core, WebGPU rendering, ~260 KB gzipped, 5× faster geometry than the next best option.
3131

32-
Works with **IFC4 / IFC4X3** (876 entities, full schema) and **IFC5 (IFCX)**. Live demo at [ifclite.com](https://www.ifclite.com/).
32+
Works with **IFC2X3**, **IFC4 / IFC4X3** and **IFC5 (IFCX)**. Live demo at [ifclite.com](https://www.ifclite.com/).
3333

3434
## Get Started
3535

@@ -238,17 +238,23 @@ Ready-to-run projects in [`examples/`](examples/):
238238
No Rust toolchain needed — WASM comes pre-built.
239239

240240
```bash
241-
GIT_LFS_SKIP_SMUDGE=1 git clone https://github.com/louistrue/ifc-lite.git
241+
git clone https://github.com/louistrue/ifc-lite.git
242242
cd ifc-lite
243243
pnpm install && pnpm build && pnpm dev # opens viewer at localhost:5173
244244
```
245245

246-
For benchmark fixtures, fetch only what you need:
246+
If you need IFC fixtures for tests, benchmarks, or stress tests, fetch them with:
247247

248248
```bash
249-
git lfs pull --include="tests/models/ara3d/AC20-FZK-Haus.ifc"
249+
pnpm fixtures # download every fixture (idempotent, hash-verified)
250+
pnpm fixtures:check # CI-friendly: exit 1 if anything is missing or stale
250251
```
251252

253+
The fixtures are stored on a GitHub Release and catalogued in
254+
[`tests/models/manifest.json`](tests/models/manifest.json) — see
255+
[`tests/models/README.md`](tests/models/README.md) for the full design and
256+
maintainer workflow.
257+
252258
See the [Contributing Guide](https://louistrue.github.io/ifc-lite/contributing/setup/) and [Release Process](RELEASE.md).
253259

254260
## Community

packages/wasm/pkg/ifc-lite.d.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1194,9 +1194,9 @@ export interface InitOutput {
11941194
readonly profileentryjs_expressId: (a: number) => number;
11951195
readonly symboliccircle_expressId: (a: number) => number;
11961196
readonly __wbg_gpuinstancedgeometryref_free: (a: number, b: number) => void;
1197-
readonly __wasm_bindgen_func_elem_1151: (a: number, b: number, c: number) => void;
1198-
readonly __wasm_bindgen_func_elem_1150: (a: number, b: number) => void;
1199-
readonly __wasm_bindgen_func_elem_1190: (a: number, b: number, c: number, d: number) => void;
1197+
readonly __wasm_bindgen_func_elem_1164: (a: number, b: number, c: number) => void;
1198+
readonly __wasm_bindgen_func_elem_1163: (a: number, b: number) => void;
1199+
readonly __wasm_bindgen_func_elem_1204: (a: number, b: number, c: number, d: number) => void;
12001200
readonly __wbindgen_export: (a: number) => void;
12011201
readonly __wbindgen_export2: (a: number, b: number, c: number) => void;
12021202
readonly __wbindgen_export3: (a: number, b: number) => number;

packages/wasm/pkg/ifc-lite.js

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -212,12 +212,12 @@ if (!('encodeInto' in cachedTextEncoder)) {
212212

213213
let WASM_VECTOR_LEN = 0;
214214

215-
function __wasm_bindgen_func_elem_1151(arg0, arg1, arg2) {
216-
wasm.__wasm_bindgen_func_elem_1151(arg0, arg1, addHeapObject(arg2));
215+
function __wasm_bindgen_func_elem_1164(arg0, arg1, arg2) {
216+
wasm.__wasm_bindgen_func_elem_1164(arg0, arg1, addHeapObject(arg2));
217217
}
218218

219-
function __wasm_bindgen_func_elem_1190(arg0, arg1, arg2, arg3) {
220-
wasm.__wasm_bindgen_func_elem_1190(arg0, arg1, addHeapObject(arg2), addHeapObject(arg3));
219+
function __wasm_bindgen_func_elem_1204(arg0, arg1, arg2, arg3) {
220+
wasm.__wasm_bindgen_func_elem_1204(arg0, arg1, addHeapObject(arg2), addHeapObject(arg3));
221221
}
222222

223223
const GeoReferenceJsFinalization = (typeof FinalizationRegistry === 'undefined')
@@ -3093,7 +3093,7 @@ function __wbg_get_imports() {
30933093
const a = state0.a;
30943094
state0.a = 0;
30953095
try {
3096-
return __wasm_bindgen_func_elem_1190(a, state0.b, arg0, arg1);
3096+
return __wasm_bindgen_func_elem_1204(a, state0.b, arg0, arg1);
30973097
} finally {
30983098
state0.a = a;
30993099
}
@@ -3205,9 +3205,9 @@ function __wbg_get_imports() {
32053205
const ret = BigInt.asUintN(64, arg0);
32063206
return addHeapObject(ret);
32073207
};
3208-
imports.wbg.__wbindgen_cast_782a03ac5d769879 = function(arg0, arg1) {
3209-
// Cast intrinsic for `Closure(Closure { dtor_idx: 151, function: Function { arguments: [Externref], shim_idx: 152, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`.
3210-
const ret = makeMutClosure(arg0, arg1, wasm.__wasm_bindgen_func_elem_1150, __wasm_bindgen_func_elem_1151);
3208+
imports.wbg.__wbindgen_cast_49a1621a976e807f = function(arg0, arg1) {
3209+
// Cast intrinsic for `Closure(Closure { dtor_idx: 154, function: Function { arguments: [Externref], shim_idx: 155, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`.
3210+
const ret = makeMutClosure(arg0, arg1, wasm.__wasm_bindgen_func_elem_1163, __wasm_bindgen_func_elem_1164);
32113211
return addHeapObject(ret);
32123212
};
32133213
imports.wbg.__wbindgen_cast_d6cd19b81560fd6e = function(arg0) {

packages/wasm/pkg/ifc-lite_bg.wasm

14.5 KB
Binary file not shown.

packages/wasm/pkg/ifc-lite_bg.wasm.d.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -215,9 +215,9 @@ export const meshcollection_rtcOffsetX: (a: number) => number;
215215
export const profileentryjs_expressId: (a: number) => number;
216216
export const symboliccircle_expressId: (a: number) => number;
217217
export const __wbg_gpuinstancedgeometryref_free: (a: number, b: number) => void;
218-
export const __wasm_bindgen_func_elem_1151: (a: number, b: number, c: number) => void;
219-
export const __wasm_bindgen_func_elem_1150: (a: number, b: number) => void;
220-
export const __wasm_bindgen_func_elem_1190: (a: number, b: number, c: number, d: number) => void;
218+
export const __wasm_bindgen_func_elem_1164: (a: number, b: number, c: number) => void;
219+
export const __wasm_bindgen_func_elem_1163: (a: number, b: number) => void;
220+
export const __wasm_bindgen_func_elem_1204: (a: number, b: number, c: number, d: number) => void;
221221
export const __wbindgen_export: (a: number) => void;
222222
export const __wbindgen_export2: (a: number, b: number, c: number) => void;
223223
export const __wbindgen_export3: (a: number, b: number) => number;

packages/wasm/pkg/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
"IFC-Lite Contributors"
66
],
77
"description": "WebAssembly bindings for IFC-Lite",
8-
"version": "2.2.0",
8+
"version": "2.3.0",
99
"license": "MPL-2.0",
1010
"repository": {
1111
"type": "git",

0 commit comments

Comments
 (0)