Skip to main content

ifc_lite_geometry/
lib.rs

1// This Source Code Form is subject to the terms of the Mozilla Public
2// License, v. 2.0. If a copy of the MPL was not distributed with this
3// file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
5//! # IFC-Lite Geometry Processing
6//!
7//! Efficient geometry processing for IFC models using [earcutr](https://docs.rs/earcutr)
8//! triangulation and [nalgebra](https://docs.rs/nalgebra) for transformations.
9//!
10//! ## Overview
11//!
12//! This crate transforms IFC geometry representations into GPU-ready triangle meshes:
13//!
14//! - **Profile Handling**: Extract and process 2D profiles (rectangle, circle, arbitrary)
15//! - **Extrusion**: Generate 3D meshes from extruded profiles
16//! - **Triangulation**: Polygon triangulation with hole support via earcutr
17//! - **CSG Operations**: Full boolean operations (difference, union, intersection)
18//! - **Mesh Processing**: Normal calculation and coordinate transformations
19//!
20//! ## Supported Geometry Types
21//!
22//! | Type | Status | Description |
23//! |------|--------|-------------|
24//! | `IfcExtrudedAreaSolid` | Full | Most common - extruded profiles |
25//! | `IfcExtrudedAreaSolidTapered` | Full | Lofted extrusion between two profiles |
26//! | `IfcFacetedBrep` | Full | Boundary representation meshes |
27//! | `IfcTriangulatedFaceSet` | Full | Pre-triangulated (IFC4) |
28//! | `IfcBooleanClippingResult` | Full | CSG operations (difference, union, intersection) |
29//! | `IfcMappedItem` | Full | Instanced geometry |
30//! | `IfcSweptDiskSolid` | Full | Pipe/tube geometry |
31//!
32//! ## Quick Start
33//!
34//! ```rust,ignore
35//! use ifc_lite_geometry::{
36//!     Profile2D, extrude_profile, triangulate_polygon,
37//!     Point2, Point3, Vector3
38//! };
39//!
40//! // Create a rectangular profile
41//! let profile = Profile2D::rectangle(2.0, 1.0);
42//!
43//! // Extrude to 3D
44//! let direction = Vector3::new(0.0, 0.0, 1.0);
45//! let mesh = extrude_profile(&profile, direction, 3.0)?;
46//!
47//! println!("Generated {} triangles", mesh.triangle_count());
48//! ```
49//!
50//! ## Geometry Router
51//!
52//! Use the [`GeometryRouter`] to automatically dispatch entities to appropriate processors:
53//!
54//! ```rust,ignore
55//! use ifc_lite_geometry::{GeometryRouter, GeometryProcessor};
56//!
57//! let router = GeometryRouter::new();
58//!
59//! // Process entity
60//! if let Some(mesh) = router.process(&decoder, &entity)? {
61//!     renderer.add_mesh(mesh);
62//! }
63//! ```
64//!
65//! ## Performance
66//!
67//! - **Simple extrusions**: ~2000 entities/sec
68//! - **Complex Breps**: ~200 entities/sec
69//! - **Boolean operations**: ~20 entities/sec
70
71// Module visibility: only 8 modules below are reached externally by sibling
72// crates via a direct submodule path (`ifc_lite_geometry::<module>::...`) and
73// must stay `pub`: csg, csg_capture, kernel, material_layer_index, mesh,
74// projection_outline, rect_fast, space_dcel. Everything else is internal
75// wiring; external consumers reach its types through the root-level `pub use`
76// re-exports below, so those modules are `pub(crate)` (see #C3.2).
77pub(crate) mod alignment;
78pub(crate) mod bool2d;
79/// General 2D booleans over contour sets (union/difference/intersection),
80/// keeping every disjoint output shape. Distinct from `bool2d`, which is the
81/// fixed single-`Profile2D` void-subtraction path. Reached through the
82/// root-level re-exports below, so it stays `pub(crate)` per #C3.2.
83pub(crate) mod contour_bool2d;
84/// Deterministic Constrained Delaunay Triangulation + bounded Ruppert
85/// min-angle refinement. Backs the quality triangulators in `triangulation`.
86mod cdt;
87pub mod csg;
88/// Measurement-only CSG corpus capture (off-by-default `csg_capture` feature).
89#[cfg(feature = "csg_capture")]
90pub mod csg_capture;
91/// Deterministic near-coplanar facet weld for faceted-BREP host meshes.
92/// Corrects f32 import jitter (~0.09°) so authored-coplanar roof slope facets
93/// are EXACTLY coplanar before the exact-kernel opening cut (issue #1007).
94pub(crate) mod facet_weld;
95/// Intra-mesh vertex weld + index dedup applied at the per-element mesh source
96/// (`build_mesh_data`), collapsing the faceted-brep per-face vertex duplication
97/// while keeping creases (distinct normals) split.
98pub mod mesh_weld;
99/// Structured-diagnostics macro shims for the `observability` feature
100/// (tracing when ON, the legacy eprintln fallback when OFF).
101pub(crate) mod diag;
102pub(crate) mod diagnostics;
103pub(crate) mod error;
104pub(crate) mod geom_hash;
105/// Shared float-noise-tolerance quantisation constants used by more than one
106/// geometry pass (single-sourced so independently-evolving passes can't drift
107/// apart on the same tolerance).
108pub(crate) mod grid;
109pub(crate) mod extrusion;
110pub(crate) mod instancing;
111/// Pure-Rust exact mesh-arrangement CSG kernel — the only CSG kernel, on
112/// every target (see docs/architecture/geometry-pipeline.md).
113pub mod kernel;
114pub mod material_layer_index;
115pub mod mesh;
116pub(crate) mod mesh_orient;
117pub(crate) mod processors;
118pub(crate) mod profile;
119pub(crate) mod profile_extractor;
120pub(crate) mod profiles;
121pub mod projection_outline;
122pub mod rect_fast;
123#[cfg(feature = "triangulation-alt")]
124pub use triangulation::alt_oracle::set_alt_triangulator;
125/// Scalar abstraction the extrusion mesher is generic over (`f64` in
126/// production, a forward-mode dual number in the B4.4 kernel-adjoint spike).
127pub(crate) mod scalar;
128/// The extrusion mesher, generic over the scalar. `extrusion`'s public
129/// functions are its `f64` instantiations.
130pub(crate) mod extrusion_generic;
131/// Profile triangulation / ring builders, generic over the scalar.
132pub(crate) mod profile_generic;
133
134/// B4.4 - the M3 kernel-adjoint spike (test-only). Runs the production
135/// extrusion mesher with a forward-mode dual scalar and grades its adjoints
136/// against central finite differences.
137#[cfg(test)]
138#[path = "b44_kernel_adjoint_tests.rs"]
139mod b44_kernel_adjoint;
140pub use rect_fast::RectFastStats;
141pub(crate) mod router;
142/// Per-element mesh simplification for the demesher (cavity removal, grid
143/// vertex-clustering decimation, bounding-box collapse).
144pub mod simplify;
145pub(crate) mod tessellation;
146pub mod space_dcel;
147pub(crate) mod transform;
148pub(crate) mod triangulation;
149pub(crate) mod void_index;
150
151// Re-export nalgebra types for convenience
152pub use nalgebra::{Point2, Point3, Vector2, Vector3};
153
154pub use bool2d::{
155    compute_signed_area, ensure_ccw, ensure_cw, is_valid_contour, point_in_contour, subtract_2d,
156    subtract_multiple_2d, subtract_multiple_2d_counted,
157};
158pub use contour_bool2d::{
159    boolean_2d, resolve_2d, sanitize as sanitize_contours, BooleanOp2D, ContourSet, Ring2D,
160};
161pub use csg::{calculate_normals, ClippingProcessor, Plane, Triangle};
162pub use diagnostics::{BoolFailure, BoolFailureReason, BoolOp};
163pub use error::{Error, Result};
164pub use geom_hash::{hash_mesh_world, GeometryHasher, DEFAULT_GEOM_HASH_TOLERANCE};
165pub use extrusion::{extrude_profile, extrude_profile_lofted, extrude_profile_with_voids};
166pub use instancing::{
167    bake_source_at_world, collate_and_encode, collate_instances, collate_refs,
168    compose_instance_world_row_major, decode_instanced, encode_instanced, encode_refs,
169    instance_rel_row_major_f32, verify_recomposition, Collated, DecodedInstance, DecodedInstanced,
170    DecodedTemplate, InstanceMeshRef, InstanceOccurrence, InstanceTemplate, INSTANCED_MAGIC,
171    INSTANCED_VERSION,
172};
173pub use material_layer_index::{
174    LayerAxis, LayerBuildup, LayerInfo, MaterialLayerFlat, MaterialLayerIndex,
175};
176pub use mesh::{InstanceMeta, Mesh, SubMesh, SubMeshCollection};
177pub use mesh_orient::orient_mesh_outward;
178pub use processors::{
179    AdvancedBrepProcessor, BooleanClippingProcessor, ExtrudedAreaSolidProcessor,
180    ExtrudedAreaSolidTaperedProcessor, FaceBasedSurfaceModelProcessor, FacetedBrepProcessor,
181    build_texture_index, ImageTextureRef, MeshTexture, PolygonalFaceSetProcessor,
182    ResolvedTextureMap, RevolvedAreaSolidProcessor, TextureAttachment, TextureSource, SurfaceOfLinearExtrusionProcessor,
183    SweptDiskSolidProcessor, TriangulatedFaceSetProcessor,
184};
185pub use alignment::{AlignmentCurve, AlignmentFrame};
186pub use profile::{Profile2D, Profile2DWithVoids, ProfileType, VoidInfo};
187pub use profile_extractor::{extract_profiles, ExtractedProfile};
188pub use profiles::ProfileProcessor;
189pub use router::take_bool2d_stats;
190pub use router::{take_prism_defers, take_prism_stats};
191pub use router::{
192    aggregate_diagnostics, local_frame_set_enabled_override, ClassificationStats,
193    GEOMETRY_DIAGNOSTICS_SCHEMA_VERSION,
194    ClassificationSummary, GeometryDiagnostics, GeometryProcessor, GeometryRouter,
195    HostOpeningDiagnostic, ItemDedupCache, MappedInstancePlan, OpeningDiagnostic, OpeningKindDiag,
196    ReasonCount, RectFastSummary, RectParam, SharedMappedItemCache, WorstHost,
197};
198
199/// The streaming / needs-shift large-coordinate threshold (metres): a world
200/// coordinate whose magnitude exceeds this needs RTC re-basing before it is
201/// cast to f32, or the model renders with vertex jitter. Shared by the router's
202/// own coordinate sampling (`router::rtc_offset`) and the streaming pre-pass
203/// meta resolver (`ifc_lite_processing::stream_meta`) so those two make the same
204/// decision. (Other 10 km checks carry their own local constant of the same
205/// value.)
206pub const LARGE_COORD_THRESHOLD_METERS: f64 = 10000.0;
207pub use simplify::{simplify_mesh, SimplifyOptions, SimplifyStats};
208pub use tessellation::{scale_segments, TessellationQuality};
209pub use transform::{
210    parse_axis2_placement_3d, parse_axis2_placement_3d_from_id, parse_cartesian_point,
211    parse_cartesian_point_from_id, parse_direction, parse_direction_from_id,
212    rotation_angle_about_z,
213};
214pub use triangulation::triangulate_polygon;
215pub use void_index::{
216    build_aggregate_children_index, compute_parts_to_skip, propagate_voids_to_parts,
217    propagate_voids_via_aggregates, VoidIndex,
218};