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 alignment_arc_length;
79pub(crate) mod alignment_axis;
80mod alignment_sampling;
81mod alignment_sampling_curve;
82mod alignment_sampling_placement;
83pub mod analytic;
84mod curve_source;
85mod trimmed_curve;
86pub(crate) mod gradient;
87pub(crate) mod curve_segment;
88pub(crate) mod bool2d;
89/// General 2D booleans over contour sets (union/difference/intersection),
90/// keeping every disjoint output shape. Distinct from `bool2d`, which is the
91/// fixed single-`Profile2D` void-subtraction path. Reached through the
92/// root-level re-exports below, so it stays `pub(crate)` per #C3.2.
93pub(crate) mod contour_bool2d;
94mod contour_composition;
95mod contour_grid_guard;
96/// Deterministic Constrained Delaunay Triangulation + bounded Ruppert
97/// min-angle refinement. Backs the quality triangulators in `triangulation`.
98mod cdt;
99mod terrain_cdt;
100/// Candidate contact normals for the `clash_solid` trust gate — the directions
101/// its thickness measurement is taken along. Internal to that gate, so it stays
102/// private; split out only to keep `clash_solid` inside the size ratchet.
103mod clash_contact_axes;
104pub mod clash_solid;
105pub mod csg;
106/// Measurement-only CSG corpus capture (off-by-default `csg_capture` feature).
107#[cfg(feature = "csg_capture")]
108pub mod csg_capture;
109/// Deterministic near-coplanar facet weld for faceted-BREP host meshes.
110/// Corrects f32 import jitter (~0.09°) so authored-coplanar roof slope facets
111/// are EXACTLY coplanar before the exact-kernel opening cut (issue #1007).
112pub(crate) mod facet_weld;
113/// Intra-mesh vertex weld + index dedup applied at the per-element mesh source
114/// (`build_mesh_data`), collapsing the faceted-brep per-face vertex duplication
115/// while keeping creases (distinct normals) split.
116pub mod mesh_weld;
117/// Structured-diagnostics macro shims for the `observability` feature
118/// (tracing when ON, the legacy eprintln fallback when OFF).
119pub(crate) mod diag;
120#[cfg(feature = "opening-perf-trace")]
121#[doc(hidden)]
122pub mod opening_perf_trace;
123pub(crate) mod diagnostics;
124pub(crate) mod error;
125pub(crate) mod geom_hash;
126/// Shared float-noise-tolerance quantisation constants used by more than one
127/// geometry pass (single-sourced so independently-evolving passes can't drift
128/// apart on the same tolerance).
129pub(crate) mod grid;
130/// Shared `[f64; 2]` plane primitives (area, point-in-polygon, exact segment
131/// intersection) used by `space_dcel`, `terrain_cdt` and `scan_outline`.
132pub(crate) mod geom2d;
133pub(crate) mod extrusion;
134pub(crate) mod instancing;
135/// Pure-Rust exact mesh-arrangement CSG kernel — the only CSG kernel, on
136/// every target (see docs/architecture/geometry-pipeline.md).
137pub mod kernel;
138pub mod material_layer_index;
139pub mod mesh;
140/// Cooperative heartbeat from long geometry work to a host that must tell a
141/// slow call from a hung one (see the module docs).
142pub mod progress;
143pub(crate) mod mesh_orient;
144pub(crate) mod processors;
145pub(crate) mod profile;
146pub(crate) mod profile_extractor;
147/// [`SkippedProfile`], split out of `profile_extractor` to keep it under
148/// its module-size ratchet.
149pub(crate) mod profile_skip;
150pub(crate) mod profiles;
151pub mod projection_outline;
152pub mod rect_fast;
153#[cfg(feature = "triangulation-alt")]
154pub use triangulation::alt_oracle::set_alt_triangulator;
155/// Scalar abstraction the extrusion mesher is generic over (`f64` in
156/// production and a forward-mode dual number in scalar-adjoint tests).
157pub(crate) mod scalar;
158/// The extrusion mesher, generic over the scalar. `extrusion`'s public
159/// functions are its `f64` instantiations.
160pub(crate) mod extrusion_generic;
161/// Profile triangulation / ring builders, generic over the scalar.
162pub(crate) mod profile_generic;
163
164/// Scalar-adjoint validation (test-only). Runs the production
165/// extrusion mesher with a forward-mode dual scalar and grades its adjoints
166/// against central finite differences.
167#[cfg(test)]
168#[path = "scalar_adjoint_tests.rs"]
169mod scalar_adjoint;
170mod telemetry_transaction;
171pub use rect_fast::RectFastStats;
172pub(crate) mod router;
173/// Vector outlines traced from a slab of scan points (#6871). Reached through
174/// the root-level re-exports below.
175pub(crate) mod scan_outline;
176/// Per-element mesh simplification for the demesher (cavity removal, grid
177/// vertex-clustering decimation, bounding-box collapse).
178pub mod simplify;
179pub(crate) mod tessellation;
180/// Test-harness helpers shared by the termination/deadlock suites (the
181/// receive-and-diagnose watchdog). Never present in a shipping build; the
182/// `test-support` feature exists so `ifc-lite-processing`'s integration tests
183/// can reach it as well.
184#[cfg(any(test, feature = "test-support"))]
185pub mod test_support;
186pub mod space_dcel;
187pub(crate) mod transform;
188pub(crate) mod triangulation;
189mod union_find;
190pub(crate) mod void_index;
191/// World-frame test fixture corpus: far-from-origin placements whose offset
192/// axis differs from the axis under test, plus the normal-projected f32
193/// noise bound they demand (the #2598/#2600/#2529 defect class).
194#[cfg(test)]
195pub(crate) mod world_frame_fixture;
196/// Cut one element into one closed solid per location zone (#2508 item 2), on
197/// top of the exact kernel rather than beside it.
198pub mod zone_split;
199
200// Re-export nalgebra types for convenience
201pub use nalgebra::{Matrix4, Point2, Point3, Vector2, Vector3};
202
203pub use bool2d::{
204    compute_signed_area, ensure_ccw, ensure_cw, is_valid_contour, point_in_contour, subtract_2d,
205    subtract_multiple_2d, subtract_multiple_2d_counted,
206};
207pub use contour_composition::FixedGridComposition;
208pub use contour_bool2d::{
209    boolean_2d, boolean_2d_fixed_grid, ContourFillRule, resolve_2d, sanitize as sanitize_contours, BooleanOp2D, ContourSet, Ring2D,
210};
211pub use clash_solid::{intersection_solid, DegenerateReason, IntersectionSolid};
212pub use union_find::UnionFind;
213pub use csg::{calculate_normals, ClippingProcessor, GroupCut, GroupReject, Plane, Triangle};
214pub use diagnostics::{BoolFailure, BoolFailureReason, BoolOp};
215pub use error::{Error, Result};
216pub use geom_hash::{
217    hash_mesh_world, GeometryClosure, GeometryHasher, DEFAULT_GEOM_HASH_TOLERANCE,
218    MIN_GEOM_HASH_TOLERANCE,
219};
220pub use extrusion::{extrude_profile, extrude_profile_lofted, extrude_profile_with_voids};
221pub use instancing::{
222    bake_source_at_world, collate_and_encode, collate_instances, collate_refs,
223    collate_refs_in_basis, collate_refs_verified_in, compose_instance_world_row_major, decode_instance_finishes, decode_instanced,
224    encode_instanced, encode_refs, encode_refs_with_finishes, instance_rel_row_major_f32, verify_recomposition, Collated,
225    DecodedInstance, DecodedInstanced, DecodedTemplate, InstanceMeshRef, InstanceOccurrence,
226    InstanceTemplate, INSTANCED_MAGIC, INSTANCED_VERSION,
227};
228pub use material_layer_index::{
229    LayerAxis, LayerBuildup, LayerInfo, MaterialLayerFlat, MaterialLayerIndex,
230};
231pub use mesh::{InstanceMeta, Mesh, SubMesh, SubMeshCollection};
232pub use mesh_orient::{orient_mesh_outward, orient_mesh_outward_verdict, OrientVerdict};
233pub use processors::{
234    AdvancedBrepProcessor, BooleanClippingProcessor, ExtrudedAreaSolidProcessor,
235    ExtrudedAreaSolidTaperedProcessor, FaceBasedSurfaceModelProcessor, FacetedBrepProcessor,
236    build_texture_index, embedded_raster_dimensions, MAX_TEXTURE_DIMENSION, ImageTextureRef, MeshTexture, PolygonalFaceSetProcessor,
237    ResolvedTextureMap, RevolvedAreaSolidProcessor, TextureAttachment, TextureSource, SurfaceOfLinearExtrusionProcessor,
238    SweptDiskSolidProcessor, TriangulatedFaceSetProcessor,
239};
240pub use alignment::{AlignmentCurve, AlignmentFrame};
241pub use alignment_axis::locate_axis_curve;
242pub use alignment_sampling::{
243    sample_alignment_axes, AlignmentAxis, AlignmentSample, AlignmentSamplingDiagnostic,
244    AlignmentSamplingDiagnosticCode, AlignmentSamplingOptions, AlignmentSamplingReport,
245    SampledAlignmentAxis,
246};
247pub use analytic::{
248    extract_analytic_extrusion, extract_analytic_profile, extract_swept_disk,
249    AnalyticCurveSegment, AnalyticExtrusion, AnalyticProfile, AnalyticProfileLoop,
250    AnalyticStatus, AnalyticSweptDisk, ProfileLoopKind,
251};
252pub use profile::{Profile2D, Profile2DWithVoids, ProfileType, VoidInfo};
253pub use profile_extractor::{extract_profiles, extract_profiles_with_diagnostics, ExtractedProfile};
254pub use profile_skip::SkippedProfile;
255pub use profiles::ProfileProcessor;
256pub use cdt::take_cdt_recovery_fallbacks;
257pub use terrain_cdt::{
258    triangulate_terrain_pslg, triangulate_terrain_pslg_with_progress, TerrainCdtError,
259    TerrainCdtMesh,
260};
261pub use kernel::plane_weld::take_plane_weld_stats;
262pub use router::take_bool2d_stats;
263pub use router::{take_prism_defers, take_prism_stats};
264pub use router::{
265    aggregate_diagnostics, count_attributed_products, format_unsupported_breakdown,
266    local_frame_set_enabled_override, meshed_representations, ClassificationStats, UNATTRIBUTED_PRODUCT_ID,
267    GEOMETRY_DIAGNOSTICS_SCHEMA_VERSION, FACETED_BREP_DEDUP_FACE_LIMIT,
268    ClassificationSummary, GeometryDiagnostics, GeometryProcessor, GeometryRouter,
269    HostOpeningDiagnostic, ItemDedupCache, MappedInstancePlan, OpeningDiagnostic, OpeningKindDiag,
270    ReasonCount, RectFastSummary, RectParam, SharedMappedItemCache, SharedBrepSignatureCache, WorstHost,
271};
272
273/// The large-coordinate threshold and its predicate, defined in
274/// `ifc_lite_core::limits` so core's own bounds scan can use them too.
275pub use ifc_lite_core::limits::{coord_is_large, LARGE_COORD_THRESHOLD_METERS};
276pub use simplify::{simplify_mesh, SimplifyOptions, SimplifyStats};
277pub use scan_outline::{
278    trace_scan_outline, PlaneFrame, ScanOutline, ScanOutlineDiagnostics, ScanOutlineOptions, MAX_CELLS_LIMIT,
279    MAX_GAP_LIMIT, MAX_SNAP_DISTANCE_CELLS, MAX_VERTEX_MOVE_CELLS, MIN_CELL_SIZE_LIMIT,
280};
281pub use tessellation::{scale_segments, TessellationQuality};
282pub use transform::{
283    parse_axis2_placement_3d, parse_axis2_placement_3d_from_id, parse_cartesian_point,
284    parse_cartesian_point_from_id, parse_direction, parse_direction_from_id,
285    rotation_angle_about_z,
286};
287pub use triangulation::triangulate_polygon;
288pub use void_index::{
289    build_aggregate_children_index, compute_parts_to_skip, propagate_voids_to_parts,
290    propagate_voids_via_aggregates, VoidIndex,
291};