Skip to main content

GeometryRouter

Struct GeometryRouter 

Source
pub struct GeometryRouter { /* private fields */ }
Expand description

Geometry router - routes entities to processors

Implementations§

Source§

impl GeometryRouter

Source

pub fn take_csg_failures(&self) -> FxHashMap<u32, Vec<BoolFailure>>

Drain the boolean / CSG failures accumulated by the void-subtraction path since the router was created (or the last take_csg_failures call). Failures are keyed by IFC product express ID — the element whose opening / clip operation tripped a fallback.

Only the router-driven CSG path (multi-layer wall sub-meshes, single-mesh apply_voids_to_mesh) is currently attributed. Standalone IfcBooleanResult chains processed via the mapped-item path don’t yet flow their failures here.

Source

pub fn take_layer_slice_diag(&self) -> Vec<(u32, &'static str)>

Drain the per-element layered-slice diagnostics gathered since the last call (wasm logs them to the browser console after each batch).

Source

pub fn csg_failure_product_count(&self) -> usize

Number of products with at least one recorded CSG failure.

Source

pub fn csg_failure_total(&self) -> usize

Total number of CSG failures across all products.

Source

pub fn host_consumed_by_void(&self, product_id: u32) -> bool

Whether a host product was fully consumed by a containing void (its opening’s real solid engulfs the host). An empty void-cut result for such a host is CORRECT — the element should render nothing — and must not trigger the un-cut fallback.

Source

pub fn take_classification_stats(&self) -> ClassificationStats

Drain and return the cumulative opening-classification counters since the router was created (or the last take_classification_stats call). The internal counters are reset to zero.

Source

pub fn take_host_opening_diagnostics( &self, ) -> FxHashMap<u32, HostOpeningDiagnostic>

Drain and return the per-host opening diagnostic map.

Source

pub fn take_rect_fast_stats(&self) -> RectFastStats

Drain and return this router’s rect_fast counters (resets them to zero).

Source

pub fn host_opening_diagnostic_count(&self) -> usize

Total number of hosts with diagnostic records (mostly for tests).

Source§

impl GeometryRouter

Source

pub fn process_element_with_material_layers( &self, element: &DecodedEntity, decoder: &mut EntityDecoder<'_>, buildup: &LayerBuildup, void_index: &FxHashMap<u32, Vec<u32>>, ) -> Result<Option<SubMeshCollection>>

Process an element into per-layer sub-meshes, subtracting any openings first.

Returns Ok(None) when the buildup isn’t sliceable (single material, constituent set, profile set, degenerate) so the caller can fall back to the existing sub-mesh-voids path without duplicating work.

Each emitted SubMesh carries the layer’s IfcMaterial entity ID as its geometry_id — callers key colour lookup on that.

Source§

impl GeometryRouter

Source

pub fn process_element( &self, element: &DecodedEntity, decoder: &mut EntityDecoder<'_>, ) -> Result<Mesh>

Process building element (IfcWall, IfcBeam, etc.) into mesh Follows the representation chain: Element → Representation → ShapeRepresentation → Items

Source

pub fn process_element_with_submeshes( &self, element: &DecodedEntity, decoder: &mut EntityDecoder<'_>, ) -> Result<SubMeshCollection>

Process element and return sub-meshes with their geometry item IDs. This preserves per-item identity for color/style lookup.

For elements with multiple styled geometry items (like windows with frames + glass), this returns separate sub-meshes that can receive different colors.

Source

pub fn process_representation_item( &self, item: &DecodedEntity, decoder: &mut EntityDecoder<'_>, ) -> Result<Mesh>

Process a single representation item (IfcExtrudedAreaSolid, etc.), with content-dedup: a 128-bit structural hash of the item subtree skips the meshing + CSG for geometry byte-identical to an item meshed earlier (e.g. the thousands of Tekla connection plates/bolts an exporter failed to share via IfcMappedItem). The cached mesh is colour-free and pre-placement; the caller keeps this item’s own geometry_id (so colour/palette/texture stay per-instance) and applies voids + placement afterwards, so a cache hit is indistinguishable from a fresh build.

Source§

impl GeometryRouter

Source

pub fn detect_rtc_offset_from_first_element<T>( &self, content: &T, decoder: &mut EntityDecoder<'_>, ) -> (f64, f64, f64)
where T: AsRef<[u8]> + ?Sized,

Detect RTC offset by scanning the file for building elements. Used by synchronous parse paths.

Source

pub fn detect_rtc_offset_from_jobs( &self, jobs: &[(u32, usize, usize, IfcType)], decoder: &mut EntityDecoder<'_>, ) -> Option<(f64, f64, f64)>

Detect RTC offset using pre-collected geometry jobs (avoids re-scanning the file). Returns None when no usable translation samples were found, allowing callers to distinguish “no shift needed” from “detection had no data”.

Source

pub fn detect_rtc_offset_with_fallback( &self, jobs: &[(u32, usize, usize, IfcType)], decoder: &mut EntityDecoder<'_>, content: &[u8], ) -> (f64, f64, f64)

Detect the RTC offset from sampled jobs, falling back to a full-file placement-bounds scan when no usable translation samples were found.

Single shared entry point for the server processing path and the wasm prepasses so both sides make the identical needs-shift decision: a model whose sampled placements fail to decode while raw geometry carries >10 km coordinates must be re-based identically everywhere (previously the wasm prepasses silently fell back to (0,0,0) and the browser rendered f32 vertex jitter that the server never saw).

Source§

impl GeometryRouter

Source

pub fn process_element_with_submeshes_textured( &self, element: &DecodedEntity, decoder: &mut EntityDecoder<'_>, texture_index: &FxHashMap<u32, ResolvedTextureMap>, ) -> Result<SubMeshCollection>

Texture-aware Self::process_element_with_submeshes (#1781): a face set listed in texture_index becomes its own textured sub-mesh carrying per-vertex UVs + the texture attachment. The void path never passes an index — a CSG cut rebuilds vertices, which would orphan the UVs, so a voided textured element renders with its style colour instead.

Source

pub fn process_representation_map( &self, rep_map: &DecodedEntity, decoder: &mut EntityDecoder<'_>, ) -> Result<Mesh>

Tessellate an IfcRepresentationMap’s MappedRepresentation and bake its MappingOrigin placement (issue #957).

Used to render geometry that hangs off an IfcTypeProduct (e.g. IfcBoilerType) through its RepresentationMaps when no occurrence instantiates it — the buildingSMART annex-E “tessellated shape with style” samples ship exactly this shape (geometry on the type, declared via IfcRelDeclares, with no product instance).

Unlike Self::process_mapped_item_cached, this applies MappingOrigin (IfcRepresentationMap attr 0) rather than a MappingTarget: there is no occurrence placement and no IfcMappedItem to carry one, so the MappingOrigin axis placement is the only transform. It is the caller’s responsibility to only invoke this for orphan representation maps so normally-instanced typed products aren’t double-rendered.

Source

pub fn process_representation_map_with_texture( &self, rep_map: &DecodedEntity, decoder: &mut EntityDecoder<'_>, texture_index: &FxHashMap<u32, ResolvedTextureMap>, ) -> Result<Vec<(Mesh, Vec<f32>, Option<TextureAttachment>)>>

Texture-aware variant of Self::process_representation_map (issue #961). Returns one render part per output mesh: each textured IfcTriangulatedFaceSet item becomes its OWN part carrying its UVs + decoded image (so a representation with several differently-textured items renders each with the correct image), and all untextured items are merged into a single part with empty UVs / no texture. The MappingOrigin placement is baked into every part.

Source§

impl GeometryRouter

Source

pub fn parametric_rect_probe_all( &self, element: &DecodedEntity, decoder: &mut EntityDecoder<'_>, ) -> Option<Vec<RectParam>>

EXACT boxes for a body that is a UNION OF RECTANGULAR PRISMS (the common Tekla multi-solid opening): one box per representation item, or None if any item is not a rectangular extrusion. The cellular rect_fast cut subtracts the N boxes natively.

Source

pub fn parametric_rect_probe( &self, element: &DecodedEntity, decoder: &mut EntityDecoder<'_>, ) -> Option<RectParam>

PHASE-0 CENSUS (read-only): the EXACT oriented rectangular box of an extruded element, read from the IFC parametrics (IfcRectangleProfileDef XDim/YDim/Depth + composed placement axes), NOT inferred from the f32 mesh. Returns None unless the element’s body is a single clean IfcRectangleProfileDef extrusion (after unwrapping IfcBooleanClippingResult / IfcMappedItem). This is the parametric frame + extents the failed oriented attempt should have used instead of infer_opening_frame + mesh-AABB.

Source

pub fn get_opening_item_meshes_world( &self, element: &DecodedEntity, decoder: &mut EntityDecoder<'_>, ) -> Result<Vec<Mesh>>

Get per-item meshes for an opening element, transformed to world coordinates. Uses the same transform_mesh path as process_element to ensure identical coordinate handling (ObjectPlacement, unit scaling, conditional RTC offset).

Source

pub fn get_opening_item_bounds_with_direction( &self, element: &DecodedEntity, decoder: &mut EntityDecoder<'_>, ) -> Result<Vec<(Point3<f64>, Point3<f64>, Option<Vector3<f64>>)>>

Extrusion direction is in world coordinates, normalized Returns None for extrusion direction if it cannot be extracted (fallback to bounds-only)

Source§

impl GeometryRouter

Source

pub fn process_element_with_voids( &self, element: &DecodedEntity, decoder: &mut EntityDecoder<'_>, void_index: &FxHashMap<u32, Vec<u32>>, ) -> Result<Mesh>

Process element with void subtraction (openings) Process element with voids using optimized plane clipping

This approach is more efficient than full 3D CSG for rectangular openings:

  1. Get chamfered wall mesh (preserves chamfered corners)
  2. For each opening, use optimized box cutting with internal face generation
  3. Apply any clipping operations (roof clips) from original representation

Process an element with void subtraction (openings).

This function handles three distinct cases for cutting openings:

  1. Floor/Slab openings (vertical Z-extrusion): Uses CSG with actual mesh geometry because the XY footprint may be rotated relative to the slab orientation.

  2. Wall openings (horizontal X/Y-extrusion, axis-aligned): Uses AABB clipping for fast, accurate cutting of rectangular openings.

  3. Diagonal wall openings: Uses AABB clipping without internal face generation to avoid rotation artifacts.

Reveal faces (inner surfaces of the opening holes) are generated as a post-clipping step for rectangular and diagonal openings. For diagonal walls the geometry is computed in a rotated axis-aligned frame and rotated back, giving correct results for any wall orientation.

Source

pub fn process_element_with_submeshes_and_voids( &self, element: &DecodedEntity, decoder: &mut EntityDecoder<'_>, void_index: &FxHashMap<u32, Vec<u32>>, ) -> Result<SubMeshCollection>

Process an element into per-item sub-meshes with opening subtraction.

Mirrors [process_element_with_voids] but preserves each IfcShapeRepresentation item as its own sub-mesh so that callers can look up a direct IfcStyledItem color per geometry item (e.g. the three extrusion layers of a multi-layer wall). The opening(s) are subtracted from each sub-mesh independently so that windows and doors cut through every material layer they intersect.

Returns an empty collection when there are no openings (callers should fall back to [process_element_with_submeshes]) or when every sub-mesh is destroyed by void subtraction.

Source§

impl GeometryRouter

Source

pub fn new() -> Self

Create new router with default processors

Source

pub fn with_units<T>(content: &T, decoder: &mut EntityDecoder<'_>) -> Self
where T: AsRef<[u8]> + ?Sized,

Create router and extract unit scale from IFC file Automatically finds IFCPROJECT and extracts length unit conversion

Source

pub fn with_units_and_rtc<T>( content: &T, decoder: &mut EntityDecoder<'_>, rtc_offset: (f64, f64, f64), ) -> Self
where T: AsRef<[u8]> + ?Sized,

Create router with unit scale extracted from IFC file AND RTC offset for large coordinates This is the recommended method for georeferenced models (Swiss UTM, etc.)

§Arguments
  • content - IFC file content
  • decoder - Entity decoder
  • rtc_offset - RTC offset to subtract from world coordinates (typically model centroid)
Source

pub fn with_scale(unit_scale: f64) -> Self

Create router with pre-calculated unit scale

Source

pub fn new_dedup_cache() -> ItemDedupCache

A fresh empty shared item-dedup cache, to be cloned into every per-element / per-batch router of ONE loaded model so they all dedup against it. Keep one per model: the key is a per-model entity-structure hash, and the cached meshes bake in this model’s unit scale / tessellation quality.

Source

pub fn build_dedup_extra_enabled() -> bool

Whether content-dedup covers EXTRA item types beyond the proven default set (faceset / surface-model families). item_signature’s generic byte walk is already complete for them, so this is pure additive build savings on models that repeat tessellated geometry — but it pays a hash on every such item, so it stays OFF by default until corpus-validated (low-reuse models would pay the hash for no payback, the #1177 trap). Opt in with IFC_LITE_DEDUP_EXTRA=1 or Self::set_build_dedup_extra_override. Default OFF keeps native==wasm identical.

Source

pub fn set_build_dedup_extra_override(v: Option<bool>)

Test-only: force Self::build_dedup_extra_enabled on/off (None = env).

Source

pub fn enable_content_dedup_shared(&mut self, cache: ItemDedupCache)

Inject a shared item-dedup cache (see Self::new_dedup_cache) into this router. All routers given the SAME Arc dedup against one cache, so byte-identical geometry is meshed once across the whole model regardless of how elements are partitioned across threads or batches.

Source

pub fn new_mapped_item_cache() -> SharedMappedItemCache

A fresh empty shared IfcMappedItem source cache, to be cloned into every per-element / per-batch router of ONE loaded model so a mapped source shared across owning elements is meshed once model-wide (#1623). Mirrors Self::new_dedup_cache. Keep one per model/pass: the cached meshes bake in this pass’s unit scale / tessellation quality.

Source

pub fn enable_shared_mapped_item_cache(&mut self, cache: SharedMappedItemCache)

Inject a shared IfcMappedItem source cache (see Self::new_mapped_item_cache) into this router. All routers given the SAME Arc mesh each unique IfcRepresentationMap source once across the whole model, instead of once per owning element (a fresh router is built per element). Takes precedence over the per-router RefCell mapped_item_cache; leaving it unset keeps the per-router fallback for single-shot callers.

Source

pub fn enable_output_instancing(&mut self, plan: MappedInstancePlan)

Arm the #1623 Phase 2 don’t-bake instancing plan (see MappedInstancePlan) on this router. Requires a shared mapped-item cache to be enabled too — the don’t-bake instance placeholders rely on the source being meshed once into it (the orphan-recovery template at finalize reads it). Leaving it unset keeps every occurrence materializing (byte-identical flat output).

Source

pub fn enable_indexed_colour_split_guard(&mut self, ids: Arc<FxHashSet<u32>>)

Arm the #858 don’t-bake exclusion set: geometry-item ids of mapped sources whose single solid carries an IfcIndexedColourMap. Such a source is routed to the flat materialize (per-palette split) instead of don’t-bake, so its occurrences keep the per-triangle palette (an instance placeholder would collapse it to one colour). Armed alongside Self::enable_output_instancing when the model has any indexed-colour maps; unset ⇒ no exclusion.

Source

pub fn set_instancing_batch_local(&mut self, on: bool)

Select the #1623 Phase 3 batch-local template mode (see Self::instancing_batch_local). The WASM per-batch path sets this so each batch materializes its OWN first-seen template per source and stays a self-contained shard; the native path leaves it off (global min-id template).

Source

pub fn disable_content_dedup(&mut self)

Disable content-dedup (drops the cache reference so item_dedup_key returns None and meshing is never skipped). Test/bench helper for an A/B against the deduped path.

Source

pub fn dedup_unique_count(&self) -> usize

Number of unique item meshes cached by content-dedup so far — the reuse the pipeline recovered (vs. the meshed-item count). Diagnostics.

Source

pub fn mapped_shared_unique_count(&self) -> usize

Number of unique IfcRepresentationMap sources meshed into the SHARED mapped-item cache so far (0 when no shared cache is injected — the RefCell fallback isn’t counted). Diagnostics, mirroring Self::dedup_unique_count; used by the #1623 A/B test to confirm the shared cache actually captured sources (so a cross-router hit is genuinely exercised, not a no-op).

Source

pub fn geometry_routing_key( &self, element: &DecodedEntity, decoder: &mut EntityDecoder<'_>, ) -> Option<u128>

Content-routing key for an element: a 128-bit structural hash of its WHOLE representation subtree (the Representation attribute, e.g. an IfcProductDefinitionShape), or None if it has no geometry. Two elements with byte-identical geometry — even renumbered — share a key, so a host can route them to the same worker; combined with the per-worker dedup cache the geometry is then meshed once per worker. Meshing-free (decode + fold) and reuses the per-router signature memo so shared sub-entities are hashed once.

(A per-instance shape-representation wrapper can make this finer than the per-ITEM dedup unit, but a true 4-router simulation showed that costs essentially no extra meshing — 1.01× — because the shared items still land on one worker, so the simpler whole-representation hash is used.)

Source

pub fn with_rtc(rtc_offset: (f64, f64, f64)) -> Self

Create router with RTC offset for large coordinate handling Use this for georeferenced models (e.g., Swiss UTM coordinates)

Source

pub fn with_scale_and_rtc(unit_scale: f64, rtc_offset: (f64, f64, f64)) -> Self

Create router with both unit scale and RTC offset

Source

pub fn with_quality(quality: TessellationQuality) -> Self

Create router with a specific tessellation quality level

Source

pub fn with_scale_and_quality( unit_scale: f64, quality: TessellationQuality, ) -> Self

Create router with both unit scale and tessellation quality

Source

pub fn set_tessellation_quality(&mut self, quality: TessellationQuality)

Set the tessellation quality level.

Reusing one router across a quality change invalidates mapped_item_cache (keyed by RepresentationMap id, not by quality), so it is cleared here to avoid serving meshes tessellated at the previous level. The other caches are content-hash keyed (geometry_hash_cache), so they stay correct.

Source

pub fn tessellation_quality(&self) -> TessellationQuality

Get the current tessellation quality level

Source

pub fn set_skip_small_cuts(&mut self, on: bool)

Set the per-build small-cut skip (#1286) and re-register the boolean / CSG / mapped processors so they carry it. Tier-independent: the viewer turns this on to skip tiny steel copes/notches for fast first paint while the tessellation tier stays at Medium (curves keep full density).

Scoped to this router instance, so a concurrent native build with the skip off is unaffected (this replaced a process-wide static that bled across builds). false (default) keeps every cut, byte-identical to before the optimization.

Source

pub fn skip_small_cuts(&self) -> bool

Get the current per-build small-cut skip flag.

Source

pub fn set_rtc_offset(&mut self, offset: (f64, f64, f64))

Set the RTC offset for large coordinate handling

Source

pub fn rtc_offset(&self) -> (f64, f64, f64)

Get the current RTC offset

Source

pub fn has_rtc_offset(&self) -> bool

Check if RTC offset is active (non-zero)

Source

pub fn unit_scale(&self) -> f64

Get the current unit scale factor

Source

pub fn set_material_layer_index(&mut self, index: Arc<MaterialLayerIndex>)

Attach a material-layer buildup index. After this, sub-mesh processing automatically slices single-solid elements whose buildup is sliceable (walls with IfcMaterialLayerSetUsage, etc.) into per-layer slabs.

Source

pub fn is_material_layer_sliceable(&self, element_id: u32) -> bool

True when element_id carries a sliceable IfcMaterialLayerSetUsage, i.e. process_element_with_submeshes would split it into per-layer sub-meshes. Lets the mesh producer render the wall as ONE solid in 3D while still emitting the per-layer slices (tagged section-only) for the 2D cut.

Source

pub fn register(&mut self, processor: Box<dyn GeometryProcessor>)

Register a geometry processor

Source

pub fn resolve_scaled_placement( &self, entity: &DecodedEntity, decoder: &mut EntityDecoder<'_>, ) -> Result<[f64; 16]>

Resolve an element’s ObjectPlacement to a scaled world-space transform matrix. Returns the 4x4 matrix as a flat column-major array of 16 f64 values. The translation component is scaled from file units to meters.

Contributed by Mathias Søndergaard (Sonderwoods/Linkajou).

Source

pub fn schema(&self) -> &IfcSchema

Get schema reference

Trait Implementations§

Source§

impl Default for GeometryRouter

Source§

fn default() -> Self

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<U> As for U

Source§

fn as_<T>(self) -> T
where T: CastFrom<U>, U: Sized,

Casts self to type T. The semantics of numeric casting with the as operator are followed, so <T as As>::as_::<U> can be used in the same way as T as U for numeric conversions. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

impl<T> Pointable for T

Source§

const ALIGN: usize

The alignment of pointer.
Source§

type Init = T

The type for initializers.
Source§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
Source§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
Source§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
Source§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<SS, SP> SupersetOf<SS> for SP
where SS: SubsetOf<SP>,

Source§

fn to_subset(&self) -> Option<SS>

The inverse inclusion map: attempts to construct self from the equivalent element of its superset. Read more
Source§

fn is_in_subset(&self) -> bool

Checks if self is actually part of its subset T (and can be converted to it).
Source§

fn to_subset_unchecked(&self) -> SS

Use with care! Same as self.to_subset but without any property checks. Always succeeds.
Source§

fn from_subset(element: &SS) -> SP

The inclusion map: converts self to the equivalent element of its superset.
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.